Skip to content
Snippets Groups Projects
Commit d44c82ec authored by OZGCloud's avatar OZGCloud
Browse files

Merge branch 'master' into OZG-5326-bescheid-refactoring-part-1

parents dd4c70c0 ff6d0a15
No related branches found
No related tags found
No related merge requests found
Showing
with 748 additions and 276 deletions
...@@ -144,6 +144,11 @@ pipeline { ...@@ -144,6 +144,11 @@ pipeline {
sh 'mvn --no-transfer-progress -s $MAVEN_SETTINGS sonar:sonar' sh 'mvn --no-transfer-progress -s $MAVEN_SETTINGS sonar:sonar'
} }
} }
dir('alfa-xdomea'){
withSonarQubeEnv('sonarqube-ozg-sh'){
sh 'mvn --no-transfer-progress -s $MAVEN_SETTINGS sonar:sonar'
}
}
} }
catch (Exception e) { catch (Exception e) {
unstable("SonarQube failed") unstable("SonarQube failed")
...@@ -192,7 +197,7 @@ pipeline { ...@@ -192,7 +197,7 @@ pipeline {
} }
} }
stage('Test, build and deploy Helm Chart') { stage('Test, build and deploy Alfa Helm Chart') {
steps { steps {
script { script {
FAILED_STAGE=env.STAGE_NAME FAILED_STAGE=env.STAGE_NAME
...@@ -204,7 +209,27 @@ pipeline { ...@@ -204,7 +209,27 @@ pipeline {
sh "helm package --version=${HELM_CHART_VERSION} ." sh "helm package --version=${HELM_CHART_VERSION} ."
deployHelmChart(HELM_CHART_VERSION) deployHelmChart(HELM_CHART_VERSION, "alfa")
}
}
}
}
stage('Test, build and deploy Alfa-Client Helm Chart') {
steps {
script {
dir('alfa-client') {
FAILED_STAGE=env.STAGE_NAME
HELM_CHART_VERSION = generateHelmChartVersion()
sh "./run_helm_test.sh"
dir('src/main/helm') {
sh "helm package --version=${HELM_CHART_VERSION} ."
deployHelmChart(HELM_CHART_VERSION, "alfa-client")
}
} }
} }
} }
...@@ -292,13 +317,13 @@ pipeline { ...@@ -292,13 +317,13 @@ pipeline {
} }
} }
Void deployHelmChart(String helmChartVersion) { Void deployHelmChart(String helmChartVersion, String app ) {
withCredentials([usernamePassword(credentialsId: 'jenkins-nexus-login', usernameVariable: 'USERNAME', passwordVariable: 'PASSWORD')]){ withCredentials([usernamePassword(credentialsId: 'jenkins-nexus-login', usernameVariable: 'USERNAME', passwordVariable: 'PASSWORD')]){
if (isReleaseBranch()) { if (isReleaseBranch()) {
result = sh script: '''curl -u $USERNAME:$PASSWORD https://nexus.ozg-sh.de/service/rest/v1/components?repository=ozg-base-apps -F file=@alfa-'''+helmChartVersion+'''.tgz''', returnStdout: true result = sh script: '''curl -u $USERNAME:$PASSWORD https://nexus.ozg-sh.de/service/rest/v1/components?repository=ozg-base-apps -F file=@'''+app+'''-'''+helmChartVersion+'''.tgz''', returnStdout: true
} }
else { else {
result = sh script: '''curl -u $USERNAME:$PASSWORD https://nexus.ozg-sh.de/service/rest/v1/components?repository=ozg-base-apps-snapshot -F file=@alfa-'''+helmChartVersion+'''.tgz''', returnStdout: true result = sh script: '''curl -u $USERNAME:$PASSWORD https://nexus.ozg-sh.de/service/rest/v1/components?repository=ozg-base-apps-snapshot -F file=@'''+app+'''-'''+helmChartVersion+'''.tgz''', returnStdout: true
} }
if (result != '') { if (result != '') {
...@@ -398,6 +423,7 @@ String getElementAccessToken() { ...@@ -398,6 +423,7 @@ String getElementAccessToken() {
Void setNewDevVersion() { Void setNewDevVersion() {
setNewGitopsVersion("dev") setNewGitopsVersion("dev")
} }
Void setNewTestVersion() { Void setNewTestVersion() {
...@@ -407,16 +433,20 @@ Void setNewTestVersion() { ...@@ -407,16 +433,20 @@ Void setNewTestVersion() {
Void setNewGitopsVersion(String environment) { Void setNewGitopsVersion(String environment) {
def envFile = "${environment}/application/values/alfa-values.yaml" def envFile = "${environment}/application/values/alfa-values.yaml"
def commitMessage = "jenkins rollout ${environment} alfa version ${IMAGE_TAG}"; def commitMessage = "jenkins rollout ${environment} alfa version ${IMAGE_TAG}";
setNewGitopsVersion(envFile, commitMessage); setNewAlfaGitopsVersion(envFile, commitMessage);
envFile = "${environment}/application/values/alfa-client-values.yaml"
commitMessage = "jenkins rollout ${environment} alfa-client version ${IMAGE_TAG}";
setNewAlfaClientGitopsVersion(envFile, commitMessage);
} }
Void setNewBarrierefreiheitVersion() { Void setNewBarrierefreiheitVersion() {
def envFile = "dev/namespace/namespaces/by-barrierefreiheit-dev.yaml" def envFile = "dev/namespace/namespaces/by-barrierefreiheit-dev.yaml"
def commitMessage = "jenkins rollout ${IMAGE_TAG} for Barrierefreiheit Dev" def commitMessage = "jenkins rollout ${IMAGE_TAG} for Barrierefreiheit Dev"
setNewGitopsVersion(envFile, commitMessage); setNewAlfaGitopsVersion(envFile, commitMessage);
} }
Void setNewGitopsVersion(String envFile, String commitMessage) { Void setNewAlfaGitopsVersion(String envFile, String commitMessage) {
dir("gitops") { dir("gitops") {
def envVersions = readYaml file: envFile def envVersions = readYaml file: envFile
...@@ -432,6 +462,22 @@ Void setNewGitopsVersion(String envFile, String commitMessage) { ...@@ -432,6 +462,22 @@ Void setNewGitopsVersion(String envFile, String commitMessage) {
} }
} }
Void setNewAlfaClientGitopsVersion(String envFile, String commitMessage) {
dir("gitops") {
def envVersions = readYaml file: envFile
envVersions.alfa_client.image.tag = IMAGE_TAG
envVersions.alfa_client.helm.version = HELM_CHART_VERSION
writeYaml file: envFile, data: envVersions, overwrite: true
if (hasValuesFileChanged(envFile)) {
sh "git add ${envFile}"
sh "git commit -m '${commitMessage}'"
}
}
}
String getCommitHash() { String getCommitHash() {
return "-${env.GIT_COMMIT.take(7)}"; return "-${env.GIT_COMMIT.take(7)}";
} }
......
...@@ -23,7 +23,10 @@ pipeline { ...@@ -23,7 +23,10 @@ pipeline {
FAILED_PARALLEL_STAGE = " " FAILED_PARALLEL_STAGE = " "
EA_BEZEICHNER = generateBezeichner("e2e-ea") EA_BEZEICHNER = generateBezeichner("e2e-ea")
MAIN_BEZEICHNER = generateBezeichner("e2e-main") MAIN_BEZEICHNER = generateBezeichner("e2e-main")
ADMIN_BEZEICHNER = generateBezeichner("e2e-admin")
SH_SUCCESS_STATUS_CODE = 0 SH_SUCCESS_STATUS_CODE = 0
KEYCLOAK_CLIENT_ADMIN_APP = "admin"
KEYCLOAK_CLIENT_ALFA_APP = "alfa"
} }
options { options {
...@@ -98,6 +101,7 @@ pipeline { ...@@ -98,6 +101,7 @@ pipeline {
script { script {
FAILED_STAGE = env.STAGE_NAME FAILED_STAGE = env.STAGE_NAME
initEnvAdminDefaultVersions()
initEnvAlfaDefaultVersions() initEnvAlfaDefaultVersions()
initEnvVorgangManagerDefaultVersions() initEnvVorgangManagerDefaultVersions()
initEnvUserManagerDefaultVersions() initEnvUserManagerDefaultVersions()
...@@ -122,6 +126,12 @@ pipeline { ...@@ -122,6 +126,12 @@ pipeline {
string(name: "AlfaImageTag", defaultValue: env.ALFA_IMAGE_TAG, trim: true), string(name: "AlfaImageTag", defaultValue: env.ALFA_IMAGE_TAG, trim: true),
string(name: "AlfaHelmChartVersion", defaultValue: env.ALFA_HELM_CHART_VERSION, trim: true), string(name: "AlfaHelmChartVersion", defaultValue: env.ALFA_HELM_CHART_VERSION, trim: true),
string(name: "AlfaHelmRepoUrl", defaultValue: env.ALFA_HELM_REPO_URL, trim: true), string(name: "AlfaHelmRepoUrl", defaultValue: env.ALFA_HELM_REPO_URL, trim: true),
string(name: "AdministrationImageTag", defaultValue: env.ADMINISTRATION_IMAGE_TAG, trim: true),
string(name: "AdministrationHelmChartVersion", defaultValue: env.ADMINISTRATION_HELM_CHART_VERSION, trim: true),
string(name: "AdministrationHelmRepoUrl", defaultValue: env.ADMINISTRATION_HELM_REPO_URL, trim: true),
string(name: "AdminClientImageTag", defaultValue: env.ADMIN_CLIENT_IMAGE_TAG, trim: true),
string(name: "AdminClientHelmChartVersion", defaultValue: env.ADMIN_CLIENT_HELM_CHART_VERSION, trim: true),
string(name: "AdminClientHelmRepoUrl", defaultValue: env.ADMIN_CLIENT_HELM_REPO_URL, trim: true),
string(name: "VorgangManagerImageTag", defaultValue: env.VORGANG_MANAGER_IMAGE_TAG, trim: true), string(name: "VorgangManagerImageTag", defaultValue: env.VORGANG_MANAGER_IMAGE_TAG, trim: true),
string(name: "VorgangManagerHelmChartVersion", defaultValue: env.VORGANG_MANAGER_HELM_CHART_VERSION, trim: true), string(name: "VorgangManagerHelmChartVersion", defaultValue: env.VORGANG_MANAGER_HELM_CHART_VERSION, trim: true),
string(name: "VorgangManagerHelmRepoUrl", defaultValue: env.VORGANG_MANAGER_HELM_REPO_URL, trim: true), string(name: "VorgangManagerHelmRepoUrl", defaultValue: env.VORGANG_MANAGER_HELM_REPO_URL, trim: true),
...@@ -165,10 +175,9 @@ pipeline { ...@@ -165,10 +175,9 @@ pipeline {
checkoutGitopsE2eBranch() checkoutGitopsE2eBranch()
deleteOzgCloudStack([env.EA_BEZEICHNER, env.MAIN_BEZEICHNER]) deleteNamespaces([env.EA_BEZEICHNER, env.MAIN_BEZEICHNER, env.ADMIN_BEZEICHNER])
generateMainNamespaceYaml() generateNamespaces()
generateEaNamespaceYaml()
pushGitopsRepo() pushGitopsRepo()
} }
...@@ -176,7 +185,7 @@ pipeline { ...@@ -176,7 +185,7 @@ pipeline {
post { post {
failure { failure {
script { script {
deleteOzgCloudStack([env.EA_BEZEICHNER, env.MAIN_BEZEICHNER]) deleteNamespaces([env.EA_BEZEICHNER, env.MAIN_BEZEICHNER, env.ADMIN_BEZEICHNER])
} }
} }
} }
...@@ -211,13 +220,14 @@ pipeline { ...@@ -211,13 +220,14 @@ pipeline {
script { script {
FAILED_STAGE = env.STAGE_NAME FAILED_STAGE = env.STAGE_NAME
waitForOzgCloudStackRollout([env.EA_BEZEICHNER, env.MAIN_BEZEICHNER]) waitForAdminRollout(env.ADMIN_BEZEICHNER)
waitForAlfaRollout([env.EA_BEZEICHNER, env.MAIN_BEZEICHNER])
} }
} }
post { post {
failure { failure {
script { script {
deleteOzgCloudStack([env.EA_BEZEICHNER, env.MAIN_BEZEICHNER]) deleteNamespaces([env.EA_BEZEICHNER, env.MAIN_BEZEICHNER, env.ADMIN_BEZEICHNER])
} }
} }
} }
...@@ -236,14 +246,14 @@ pipeline { ...@@ -236,14 +246,14 @@ pipeline {
} }
} }
// stage('Run E2E-Tests') { stage('Run E2E-Tests') {
// when { when {
// expression { !SKIP_RUN } expression { !SKIP_RUN }
// } }
// failFast false failFast false
// parallel { parallel {
stage('E2E-EA') { stage('E2E-Alfa-EA') {
when { when {
expression { !SKIP_RUN } expression { !SKIP_RUN }
} }
...@@ -254,7 +264,7 @@ pipeline { ...@@ -254,7 +264,7 @@ pipeline {
Integer mongoDbPort = forwardMongoDbPort(generateNamespace(bezeichner)) Integer mongoDbPort = forwardMongoDbPort(generateNamespace(bezeichner))
runTests(bezeichner, 'einheitlicher-ansprechpartner', mongoDbPort, env.STAGE_NAME) runTests(bezeichner, 'alfa-e2e', 'einheitlicher-ansprechpartner', env.KEYCLOAK_CLIENT_ALFA_APP, mongoDbPort, env.STAGE_NAME)
stopForwardMongoDbPort(generateNamespace(bezeichner)) stopForwardMongoDbPort(generateNamespace(bezeichner))
} }
...@@ -268,13 +278,13 @@ pipeline { ...@@ -268,13 +278,13 @@ pipeline {
} }
always { always {
script { script {
publishE2ETestResult("einheitlicher-ansprechpartner", "Alfa E2E-Tests EA") publishAlfaE2ETestResult("einheitlicher-ansprechpartner", "Alfa E2E-Tests EA")
} }
} }
} }
} }
stage('E2E-Main') { stage('E2E-Alfa-Main') {
when { when {
expression { !SKIP_RUN } expression { !SKIP_RUN }
} }
...@@ -285,7 +295,37 @@ pipeline { ...@@ -285,7 +295,37 @@ pipeline {
Integer mongoDbPort = forwardMongoDbPort(generateNamespace(bezeichner)) Integer mongoDbPort = forwardMongoDbPort(generateNamespace(bezeichner))
runTests(bezeichner, 'main-tests', mongoDbPort, env.STAGE_NAME) runTests(bezeichner, 'alfa-e2e', 'main-tests', env.KEYCLOAK_CLIENT_ALFA_APP, mongoDbPort, env.STAGE_NAME)
stopForwardMongoDbPort(generateNamespace(bezeichner))
}
}
}
post {
failure {
script {
FAILED_PARALLEL_STAGE += "${env.STAGE_NAME} "
}
}
always {
script {
publishAlfaE2ETestResult("main-tests", "Alfa E2E-Tests main")
}
}
}
}
stage('E2E-Admin-Main') {
when {
expression { !SKIP_RUN }
}
steps {
catchError(buildResult: 'FAILURE', stageResult: 'FAILURE') {
script {
String bezeichner = env.ADMIN_BEZEICHNER
Integer mongoDbPort = forwardMongoDbPort(generateNamespace(bezeichner))
runTests(bezeichner, 'admin-e2e', 'main-tests', env.KEYCLOAK_CLIENT_ADMIN_APP, mongoDbPort, env.STAGE_NAME)
stopForwardMongoDbPort(generateNamespace(bezeichner)) stopForwardMongoDbPort(generateNamespace(bezeichner))
} }
...@@ -299,13 +339,13 @@ pipeline { ...@@ -299,13 +339,13 @@ pipeline {
} }
always { always {
script { script {
publishE2ETestResult("main-tests", "Alfa E2E-Tests main") publishAdminE2ETestResult()
}
}
} }
} }
} }
} }
// }
// }
stage('Delete E2E Namespaces') { stage('Delete E2E Namespaces') {
when { when {
...@@ -315,7 +355,7 @@ pipeline { ...@@ -315,7 +355,7 @@ pipeline {
script { script {
FAILED_STAGE = env.STAGE_NAME FAILED_STAGE = env.STAGE_NAME
deleteOzgCloudStack([env.EA_BEZEICHNER, env.MAIN_BEZEICHNER]) deleteNamespaces([env.EA_BEZEICHNER, env.MAIN_BEZEICHNER, env.ADMIN_BEZEICHNER])
} }
} }
} }
...@@ -376,21 +416,6 @@ String getHelmChartVersion(Map applicationValues) { ...@@ -376,21 +416,6 @@ String getHelmChartVersion(Map applicationValues) {
return applicationValues.helm.version return applicationValues.helm.version
} }
Void initEnvAlfaDefaultVersions() {
if (isMasterBranch() || isReleaseBranch()) {
values = getApplicationValues('alfa')
env.ALFA_IMAGE_TAG = getImageTag(values)
env.ALFA_HELM_CHART_VERSION = getHelmChartVersion(values)
}
else {
env.ALFA_IMAGE_TAG = getFeatureBranchImageTag()
env.ALFA_HELM_CHART_VERSION = getFeatureBranchHelmChartVersion()
}
env.ALFA_HELM_REPO_URL = getHelmRepoUrl()
}
Boolean isMasterBranch() { Boolean isMasterBranch() {
return env.BRANCH_NAME == 'master' return env.BRANCH_NAME == 'master'
} }
...@@ -417,6 +442,33 @@ String getRootPomVersion() { ...@@ -417,6 +442,33 @@ String getRootPomVersion() {
return rootPom.version return rootPom.version
} }
Void initEnvAdminDefaultVersions() {
def values = getApplicationValues('administration')
env.ADMINISTRATION_IMAGE_TAG = getImageTag(values)
env.ADMINISTRATION_HELM_CHART_VERSION = getHelmChartVersion(values)
env.ADMINISTRATION_HELM_REPO_URL = getHelmRepoUrl()
values = getApplicationValues('admin-client')
env.ADMIN_CLIENT_IMAGE_TAG = getImageTag(values)
env.ADMIN_CLIENT_HELM_CHART_VERSION = getHelmChartVersion(values)
env.ADMIN_CLIENT_HELM_REPO_URL = getHelmRepoUrl()
}
Void initEnvAlfaDefaultVersions() {
if (isMasterBranch() || isReleaseBranch()) {
values = getApplicationValues('alfa')
env.ALFA_IMAGE_TAG = getImageTag(values)
env.ALFA_HELM_CHART_VERSION = getHelmChartVersion(values)
}
else {
env.ALFA_IMAGE_TAG = getFeatureBranchImageTag()
env.ALFA_HELM_CHART_VERSION = getFeatureBranchHelmChartVersion()
}
env.ALFA_HELM_REPO_URL = getHelmRepoUrl()
}
Void initEnvVorgangManagerDefaultVersions() { Void initEnvVorgangManagerDefaultVersions() {
def values = getApplicationValues('vorgang-manager') def values = getApplicationValues('vorgang-manager')
...@@ -437,6 +489,12 @@ Void initEnvUserVersions(userVersions) { ...@@ -437,6 +489,12 @@ Void initEnvUserVersions(userVersions) {
env.ALFA_IMAGE_TAG = userVersions.AlfaImageTag env.ALFA_IMAGE_TAG = userVersions.AlfaImageTag
env.ALFA_HELM_CHART_VERSION = userVersions.AlfaHelmChartVersion env.ALFA_HELM_CHART_VERSION = userVersions.AlfaHelmChartVersion
env.ALFA_HELM_REPO_URL = userVersions.AlfaHelmRepoUrl env.ALFA_HELM_REPO_URL = userVersions.AlfaHelmRepoUrl
env.ADMINISTRATION_IMAGE_TAG = userVersions.AdministrationImageTag
env.ADMINISTRATION_HELM_CHART_VERSION = userVersions.AdministrationHelmChartVersion
env.ADMINISTRATION_HELM_REPO_URL = userVersions.AdministrationHelmRepoUrl
env.ADMIN_CLIENT_IMAGE_TAG = userVersions.AdminClientImageTag
env.ADMIN_CLIENT_HELM_CHART_VERSION = userVersions.AdminClientHelmChartVersion
env.ADMIN_CLIENT_HELM_REPO_URL = userVersions.AdminClientHelmRepoUrl
env.VORGANG_MANAGER_IMAGE_TAG = userVersions.VorgangManagerImageTag env.VORGANG_MANAGER_IMAGE_TAG = userVersions.VorgangManagerImageTag
env.VORGANG_MANAGER_HELM_CHART_VERSION = userVersions.VorgangManagerHelmChartVersion env.VORGANG_MANAGER_HELM_CHART_VERSION = userVersions.VorgangManagerHelmChartVersion
env.VORGANG_MANAGER_HELM_REPO_URL = userVersions.VorgangManagerHelmRepoUrl env.VORGANG_MANAGER_HELM_REPO_URL = userVersions.VorgangManagerHelmRepoUrl
...@@ -468,15 +526,43 @@ Void checkoutGitopsE2eBranch() { ...@@ -468,15 +526,43 @@ Void checkoutGitopsE2eBranch() {
} }
} }
Void generateEaNamespaceYaml() { Void generateNamespaces() {
generateNamespaceYaml(env.EA_BEZEICHNER, "by-ea-dev.yaml"); def y1 = generateAdminNamespaceYaml()
def y2 = generateEaNamespaceYaml()
def y3 = generateMainNamespaceYaml()
dir("gitops") {
sh "git add ${y1} ${y2} ${y3}"
sh "git commit -m 'add e2e namespaces for testrun'"
} }
Void generateMainNamespaceYaml() {
generateNamespaceYaml(env.MAIN_BEZEICHNER, "by-main-dev.yaml");
} }
Void generateNamespaceYaml(String bezeichner, String valuesPathSuffix) { Void generateAdminNamespaceYaml() {
def bezeichner = env.ADMIN_BEZEICHNER
def envValues
dir("alfa-client/apps/admin-e2e/") {
envValues = readYaml file: "src/fixtures/argocd/by-admin-dev.yaml";
envValues.ozgcloud.bezeichner = bezeichner
envValues.administration.put("image", ['tag': env.ADMINISTRATION_IMAGE_TAG])
envValues.administration.put("helm", ['version': env.ADMINISTRATION_HELM_CHART_VERSION, 'repoUrl': env.ADMINISTRATION_HELM_REPO_URL])
envValues.admin_client.put("image", ['tag': env.ADMIN_CLIENT_IMAGE_TAG])
envValues.admin_client.put("helm", ['version': env.ADMIN_CLIENT_HELM_CHART_VERSION, 'repoUrl': env.ADMIN_CLIENT_HELM_REPO_URL])
}
return writeYamlToGitOps(bezeichner, envValues);
}
String generateEaNamespaceYaml() {
return generateNamespaceYaml(env.EA_BEZEICHNER, "by-ea-dev.yaml");
}
String generateMainNamespaceYaml() {
return generateNamespaceYaml(env.MAIN_BEZEICHNER, "by-main-dev.yaml");
}
String generateNamespaceYaml(String bezeichner, String valuesPathSuffix) {
def envValues def envValues
dir('alfa-client/apps/alfa-e2e/') { dir('alfa-client/apps/alfa-e2e/') {
envValues = readYaml file: "src/fixtures/argocd/" + valuesPathSuffix; envValues = readYaml file: "src/fixtures/argocd/" + valuesPathSuffix;
...@@ -491,18 +577,20 @@ Void generateNamespaceYaml(String bezeichner, String valuesPathSuffix) { ...@@ -491,18 +577,20 @@ Void generateNamespaceYaml(String bezeichner, String valuesPathSuffix) {
envValues.user_manager.put("image", ['tag': env.USER_MANAGER_IMAGE_TAG]) envValues.user_manager.put("image", ['tag': env.USER_MANAGER_IMAGE_TAG])
envValues.user_manager.put("helm", ['version': env.USER_MANAGER_HELM_CHART_VERSION, 'repoUrl': env.USER_MANAGER_HELM_REPO_URL]) envValues.user_manager.put("helm", ['version': env.USER_MANAGER_HELM_CHART_VERSION, 'repoUrl': env.USER_MANAGER_HELM_REPO_URL])
} }
return writeYamlToGitOps(bezeichner, envValues);
}
writeYaml file: "gitops/dev/namespace/namespaces/by-${bezeichner}-dev.yaml", data: envValues, overwrite: true String writeYamlToGitOps(String bezeichner, Object envValues){
def bezeichnerYaml = "dev/namespace/namespaces/by-${bezeichner}-dev.yaml"
sh "cat gitops/dev/namespace/namespaces/by-${bezeichner}-dev.yaml" writeYaml file: "gitops/${bezeichnerYaml}", data: envValues, overwrite: true
dir("gitops") { sh "cat gitops/${bezeichnerYaml}"
sh "git add dev/namespace/namespaces/by-${bezeichner}-dev.yaml"
sh "git commit -m 'add e2e by-${bezeichner}-dev'" return bezeichnerYaml;
}
} }
Void deleteOzgCloudStack(ozgCloudBezeichner) { Void deleteNamespaces(ozgCloudBezeichner) {
for(bezeichner in ozgCloudBezeichner) { for(bezeichner in ozgCloudBezeichner) {
if (hasNamespaceFile(bezeichner)) { if (hasNamespaceFile(bezeichner)) {
removeNamespaceFile(bezeichner) removeNamespaceFile(bezeichner)
...@@ -536,13 +624,19 @@ Void waitForDeletion(String bezeichner) { ...@@ -536,13 +624,19 @@ Void waitForDeletion(String bezeichner) {
} }
} }
Void waitForOzgCloudStackRollout(ozgCloudBezeichner) { Void waitForAlfaRollout(ozgCloudBezeichner) {
for(bezeichner in ozgCloudBezeichner) { for(bezeichner in ozgCloudBezeichner) {
waitForRollout(bezeichner) waitForAlfaRollout(bezeichner)
} }
} }
Void waitForRollout(String bezeichner) { Void waitForAdminRollout(String bezeichner) {
waitForAlfaRollout([bezeichner])
waitForHealthyApplication(bezeichner, 'administration')
waitForHealthyApplication(bezeichner, 'admin-client')
}
Void waitForAlfaRollout(String bezeichner) {
waitForHealthyApplication(bezeichner, 'application') waitForHealthyApplication(bezeichner, 'application')
waitForHealthyApplication(bezeichner, 'vorgang-manager') waitForHealthyApplication(bezeichner, 'vorgang-manager')
waitForHealthyApplication(bezeichner, 'user-manager') waitForHealthyApplication(bezeichner, 'user-manager')
...@@ -580,26 +674,35 @@ Void waitForHealthyStatus(String bezeichner, String application) { ...@@ -580,26 +674,35 @@ Void waitForHealthyStatus(String bezeichner, String application) {
sh "kubectl wait --for=jsonpath='{.status.health.status}'=Healthy applications/by-${bezeichner}-dev-${application} -n argocd --timeout=900s" sh "kubectl wait --for=jsonpath='{.status.health.status}'=Healthy applications/by-${bezeichner}-dev-${application} -n argocd --timeout=900s"
} }
Void publishE2ETestResult(String reportFolder, String reportName) { Void publishAlfaE2ETestResult(String appVariant, String reportName) {
publishE2ETestResult("alfa-e2e", appVariant, reportName);
}
Void publishAdminE2ETestResult() {
publishE2ETestResult("admin-e2e", "main-tests", "Admin E2E-Tests main");
}
Void publishE2ETestResult(String appName, String appVariant, String reportName) {
def reportDir = "alfa-client/apps/"+appName+"/reports/"+appVariant;
publishHTML ( publishHTML (
target: [ target: [
allowMissing: false, allowMissing: false,
alwaysLinkToLastBuild: false, alwaysLinkToLastBuild: false,
keepAll: true, keepAll: true,
reportDir: "alfa-client/apps/alfa-e2e/reports/${reportFolder}", reportDir: reportDir,
reportFiles: 'report.html', reportFiles: 'report.html',
reportName: reportName reportName: reportName
] ]
) )
} }
String runTests(String bezeichner, String reportFolder, Integer dbPort, String stageName) { String runTests(String bezeichner, String appName, String appVariant, String keycloakClientName, Integer dbPort, String stageName) {
def config = generateCypressConfig(bezeichner, reportFolder, dbPort) def config = generateCypressConfig(bezeichner, appName, appVariant, keycloakClientName, dbPort)
try { try {
dir('alfa-client'){ dir('alfa-client'){
sh "npm run cypress:version" sh "npm run cypress:version"
sh "apps/alfa-e2e/run-tests.sh ${reportFolder} ${config}" sh "apps/run-tests.sh ${appName} ${appVariant} ${config}"
} }
} catch (Exception e) { } catch (Exception e) {
printNpmDebugLog() printNpmDebugLog()
...@@ -636,26 +739,27 @@ String cutBranchNameForKeycloakRealm(String branchName, String stageName) { ...@@ -636,26 +739,27 @@ String cutBranchNameForKeycloakRealm(String branchName, String stageName) {
return branchName.take(cutBranchNamePosition) return branchName.take(cutBranchNamePosition)
} }
String generateCypressConfig(String bezeichner, String testFolder, Integer dbPort) { String generateCypressConfig(String bezeichner, String appName, String appVariant, String keycloakClientName, Integer dbPort) {
def namespace = generateNamespace(bezeichner) def namespace = generateNamespace(bezeichner)
def configName = "cypress-ci-"+testFolder+".json" def configName = "cypress-ci-"+appVariant+".json"
dir('alfa-client/apps/alfa-e2e/'){ dir("alfa-client/apps/${appName}/"){
def config = readJSON file: 'cypress-ci.json' def config = readJSON file: 'cypress-ci.json'
def vorgangManagerDatabaseSecret = getVorgangManagerDatabaseSecret(namespace); def vorgangManagerDatabaseSecret = getVorgangManagerDatabaseSecret(namespace);
def decodedPassword = decodeString(vorgangManagerDatabaseSecret.password); def decodedPassword = decodeString(vorgangManagerDatabaseSecret.password);
def parsablePassword = makePasswordUrlConform(decodedPassword); def parsablePassword = makePasswordUrlConform(decodedPassword);
config.baseUrl = "https://${bezeichner}.${env.CLUSTER_BASE_URL}" as String config.baseUrl = "https://${generateUrlBezeichner(bezeichner, appName)}.${env.CLUSTER_BASE_URL}" as String
config.env.dbUrl = "mongodb://${decodeString(vorgangManagerDatabaseSecret.username)}:${parsablePassword}@localhost:${dbPort}/admin?ssl=false&directConnection=true&socketTimeoutMS=30000&heartbeatFrequencyMS=10000" as String config.env.dbUrl = "mongodb://${decodeString(vorgangManagerDatabaseSecret.username)}:${parsablePassword}@localhost:${dbPort}/admin?ssl=false&directConnection=true&socketTimeoutMS=30000&heartbeatFrequencyMS=10000" as String
config.env.keycloakUrl = "https://${env.SSO_URL}/" as String config.env.keycloakUrl = "https://${env.SSO_URL}/" as String
config.env.keycloakRealm = namespace as String config.env.keycloakRealm = namespace as String
config.videosFolder = "./reports/${testFolder}/videos" as String config.env.keycloakClient = keycloakClientName as String
config.screenshotsFolder = "./reports/${testFolder}/screenshots" as String config.videosFolder = "./reports/${appVariant}/videos" as String
config.reporterOptions.reportDir = "./reports/${testFolder}/mochawesome-report" as String config.screenshotsFolder = "./reports/${appVariant}/screenshots" as String
config.reporterOptions.reportDir = "./reports/${appVariant}/mochawesome-report" as String
config.specPattern = "src/e2e/${testFolder}/**/*.cy.{js,jsx,ts,tsx}" as String config.specPattern = "src/e2e/${appVariant}/**/*.cy.{js,jsx,ts,tsx}" as String
config.env.put("search", getElasticsearchEnv(namespace)) config.env.put("search", getElasticsearchEnv(namespace))
config.env.put("userManager", getUserManagerEnv(namespace, dbPort)) config.env.put("userManager", getUserManagerEnv(namespace, dbPort))
...@@ -666,9 +770,18 @@ String generateCypressConfig(String bezeichner, String testFolder, Integer dbPor ...@@ -666,9 +770,18 @@ String generateCypressConfig(String bezeichner, String testFolder, Integer dbPor
sh "cat ${configName}" sh "cat ${configName}"
} }
return "cypress-ci-"+testFolder+".config.ts" return "cypress-ci-"+appVariant+".config.ts"
} }
String generateUrlBezeichner(String bezeichner, String appName){
if(appName == 'admin-e2e'){
return "${bezeichner}-admin";
}
return bezeichner;
}
String makePasswordUrlConform(String password) { String makePasswordUrlConform(String password) {
return sh (script: "printf %s ${password} | jq -sRr @uri", returnStdout: true); return sh (script: "printf %s ${password} | jq -sRr @uri", returnStdout: true);
} }
......
## Allgemein
Dieses Teilprojekt enthält die End-2-End Tests für die Admin-Anwendung.
Der Aufbau ist analog der [E2E-Tests für Alfa](../alfa-e2e/README.md).
Bei Ausführung mit Jenkins im Cluster wird das [Jenkinsfile.e2e](../../Jenkinsfile.e2e) verwendet.
Bei lokaler Ausführung die hier abgelegte [docker-compose.yml](docker-compose.yml) verwenden.
import { nxE2EPreset } from '@nx/cypress/plugins/cypress-preset';
import { defineConfig } from 'cypress';
//Cypress config is generated by JenkinsFile
const cypressConfig = require('./cypress-ci-main-tests.json');
const cypressEvents = require('./src/support/cypress-tasks.ts');
export default defineConfig({
e2e: {
...nxE2EPreset(__dirname),
...cypressConfig,
setupNodeEvents(on, config) {
return cypressEvents(on, config);
},
},
retries: {
experimentalStrategy: 'detect-flake-and-pass-on-threshold',
experimentalOptions: {
maxRetries: 2,
passesRequired: 1,
},
openMode: true,
runMode: true,
},
});
{
"env": {
"database": "vorgang-manager-database",
"keycloakClient": "admin"
},
"fileServerFolder": ".",
"fixturesFolder": "./src/fixtures",
"modifyObstructiveCode": false,
"video": true,
"chromeWebSecurity": false,
"reporter": "../../node_modules/cypress-mochawesome-reporter",
"defaultCommandTimeout": 10000,
"supportFile": "./src/support/e2e.ts",
"testIsolation": false,
"reporterOptions": {
"html": false,
"json": true,
"quite": true,
"reportFilename": "report",
"overwrite": false
}
}
...@@ -19,7 +19,7 @@ ...@@ -19,7 +19,7 @@
"reporterOptions": { "reporterOptions": {
"html": false, "html": false,
"json": true, "json": true,
"reportDir": "./reports/mochawesome-report", "reportDir": "./reports/main-tests/mochawesome-report",
"reportFilename": "report", "reportFilename": "report",
"overwrite": true "overwrite": true
} }
......
import 'cypress-real-events';
import { exist } from '../../support/cypress.util'; import { exist } from '../../support/cypress.util';
export class BenutzerE2EComponent { export class BenutzerE2EComponent {
private readonly benutzerHinzufuegenButton: string = 'add-user-button'; private readonly benutzerHinzufuegenButton: string = 'Add-user-button';
private readonly userEntry: string = 'user-entry-'; private readonly userEntry: string = 'User-entry-';
private readonly userVorname: string = 'Vorname-text-input';
private readonly userNachname: string = 'Nachname-text-input';
private readonly userBenutzername: string = 'Benutzername-text-input';
private readonly userMail: string = 'E-Mail-text-input';
private readonly addOEButton: string = 'Add-organisationseinheit-button';
private readonly adminCheckbox: string = 'Admin-checkbox-editor';
private readonly loeschenCheckbox: string = 'Loschen-checkbox-editor';
private readonly userCheckbox: string = 'User-checkbox-editor';
private readonly postCheckbox: string = 'Poststelle-checkbox-editor';
public getHinzufuegenButton(): Cypress.Chainable<Element> { public getHinzufuegenButton(): Cypress.Chainable<Element> {
return cy.getTestElement(this.benutzerHinzufuegenButton); return cy.getTestElement(this.benutzerHinzufuegenButton);
...@@ -22,4 +32,60 @@ export class BenutzerE2EComponent { ...@@ -22,4 +32,60 @@ export class BenutzerE2EComponent {
exist(cy.contains(phrase)); exist(cy.contains(phrase));
}); });
} }
public getVornameInput(): Cypress.Chainable<Element> {
return cy.getTestElement(this.userVorname);
}
public getNachnameInput(): Cypress.Chainable<Element> {
return cy.getTestElement(this.userNachname);
}
public getBenutzernameInput(): Cypress.Chainable<Element> {
return cy.getTestElement(this.userBenutzername);
}
public getMailInput(): Cypress.Chainable<Element> {
return cy.getTestElement(this.userMail);
}
public getOEButton(): Cypress.Chainable<Element> {
return cy.getTestElement(this.addOEButton);
}
public addOrganisationseinheit(): void {
this.getOEButton().click();
}
public getAdminCheckbox(): Cypress.Chainable<Element> {
return cy.getTestElement(this.adminCheckbox);
}
public clickAdminCheckbox(): void {
this.getAdminCheckbox().click();
}
public getLoeschenCheckbox() {
return cy.getTestElement(this.loeschenCheckbox);
}
public clickLoeschenCheckbox(): void {
this.getLoeschenCheckbox().click();
}
public getUserCheckbox(): Cypress.Chainable<Element> {
return cy.getTestElement(this.userCheckbox);
}
public clickUserCheckbox(): void {
this.getUserCheckbox().click();
}
public getPostCheckbox(): Cypress.Chainable<Element> {
return cy.getTestElement(this.postCheckbox);
}
public clickPostCheckbox(): void {
this.getPostCheckbox().click();
}
} }
export class BuildInfoE2EComponent { export class BuildInfoE2EComponent {
private readonly locatorVersion: string = 'build-version'; private readonly locatorBuildInfo: string = 'build-info';
private readonly locatorBuildTime: string = 'build-time'; private readonly locatorBuildTime: string = 'build-time';
public getVersion() { public getBuildInfo() {
return cy.getTestElement(this.locatorVersion); return cy.getTestElement(this.locatorBuildInfo);
} }
public getBuildTime() { public getBuildTime() {
......
import { HeaderE2EComponent } from '../../page-objects/header.po'; import { HeaderE2EComponent } from '../../../page-objects/header.po';
import { MainPage } from '../../page-objects/main.po'; import { MainPage } from '../../../page-objects/main.po';
import { reload } from '../../support/cypress-helper'; import { reload } from '../../../support/cypress-helper';
import { exist } from '../../support/cypress.util'; import { exist } from '../../../support/cypress.util';
import { loginByUi } from '../../support/user-util'; import { loginByUi } from '../../../support/user-util';
describe('Login and Logout', () => { describe('Login and Logout', () => {
const mainPage: MainPage = new MainPage(); const mainPage: MainPage = new MainPage();
......
import { BuildInfoE2EComponent } from '../../components/buildinfo/buildinfo.e2e.component'; import { BuildInfoE2EComponent } from '../../../components/buildinfo/buildinfo.e2e.component';
import { HeaderE2EComponent } from '../../page-objects/header.po'; import { HeaderE2EComponent } from '../../../page-objects/header.po';
import { MainPage } from '../../page-objects/main.po'; import { MainPage } from '../../../page-objects/main.po';
import { exist } from '../../support/cypress.util'; import { exist } from '../../../support/cypress.util';
import { loginAsAriane } from '../../support/user-util'; import { loginAsAriane } from '../../../support/user-util';
describe('Buildinfo', () => { describe('Buildinfo', () => {
const mainPage: MainPage = new MainPage(); const mainPage: MainPage = new MainPage();
...@@ -14,8 +14,8 @@ describe('Buildinfo', () => { ...@@ -14,8 +14,8 @@ describe('Buildinfo', () => {
}); });
describe('get buildinfo', () => { describe('get buildinfo', () => {
it('should show version', () => { it('should show build info', () => {
exist(buildInfo.getVersion()); exist(buildInfo.getBuildInfo());
header.getCurrentUserProfile().logout(); header.getCurrentUserProfile().logout();
}); });
}); });
......
import { BenutzerE2EComponent } from '../../components/benutzer/benutzer.e2e.component'; import { BenutzerE2EComponent } from '../../components/benutzer/benutzer.e2e.component';
import { MainPage } from '../../page-objects/main.po'; import { MainPage } from '../../page-objects/main.po';
import { exist } from '../../support/cypress.util'; import { beChecked, beEnabled, exist, notBeChecked, notBeEnabled } from '../../support/cypress.util';
import { loginAsAriane } from '../../support/user-util'; import { loginAsAriane } from '../../support/user-util';
const mainPage: MainPage = new MainPage(); const mainPage: MainPage = new MainPage();
...@@ -9,7 +9,7 @@ const role1: string = 'VERWALTUNG_USER'; ...@@ -9,7 +9,7 @@ const role1: string = 'VERWALTUNG_USER';
const role2: string = 'VERWALTUNG_LOESCHEN'; const role2: string = 'VERWALTUNG_LOESCHEN';
const role3: string = 'VERWALTUNG_POSTSTELLE'; const role3: string = 'VERWALTUNG_POSTSTELLE';
const orga1: string = 'Ordnungsamt'; const orga1: string = 'Ordnungsamt';
const orga2: string = 'Landesamt für Denkmalpflege'; const orga2: string = 'Denkmalpflege';
const orga3: string = 'Wirtschaftsförderung'; const orga3: string = 'Wirtschaftsförderung';
const orga_none: string = 'keine zuständige Stelle zugewiesen'; const orga_none: string = 'keine zuständige Stelle zugewiesen';
const mail1: string = 'peter.von.der.post@ozg-sh.de'; const mail1: string = 'peter.von.der.post@ozg-sh.de';
...@@ -37,4 +37,65 @@ describe('Benutzer und Rollen', () => { ...@@ -37,4 +37,65 @@ describe('Benutzer und Rollen', () => {
benutzerPage.stringExistsInUserEntry(mail1, 'peter'); benutzerPage.stringExistsInUserEntry(mail1, 'peter');
benutzerPage.stringExistsInUserEntry(role3, 'peter'); benutzerPage.stringExistsInUserEntry(role3, 'peter');
}); });
it('should show single user screen on click', () => {
benutzerPage.addUser();
exist(benutzerPage.getVornameInput());
exist(benutzerPage.getNachnameInput());
exist(benutzerPage.getBenutzernameInput());
exist(benutzerPage.getMailInput());
notBeChecked(benutzerPage.getAdminCheckbox());
notBeChecked(benutzerPage.getLoeschenCheckbox());
notBeChecked(benutzerPage.getUserCheckbox());
notBeChecked(benutzerPage.getPostCheckbox());
});
it('should activate loeschen checkbox and deactivate the other two checkboxes', () => {
benutzerPage.clickLoeschenCheckbox();
beChecked(benutzerPage.getLoeschenCheckbox());
notBeEnabled(benutzerPage.getUserCheckbox());
notBeEnabled(benutzerPage.getPostCheckbox());
benutzerPage.clickLoeschenCheckbox();
notBeChecked(benutzerPage.getLoeschenCheckbox());
beEnabled(benutzerPage.getUserCheckbox());
beEnabled(benutzerPage.getPostCheckbox());
});
it('should additionally activate and deactivate admin checkbox', () => {
benutzerPage.clickLoeschenCheckbox();
benutzerPage.clickAdminCheckbox();
beChecked(benutzerPage.getLoeschenCheckbox());
beChecked(benutzerPage.getAdminCheckbox());
benutzerPage.clickAdminCheckbox();
notBeChecked(benutzerPage.getAdminCheckbox());
});
it('should activate user checkbox and deactivate the other two checkboxes', () => {
benutzerPage.clickLoeschenCheckbox();
benutzerPage.clickUserCheckbox();
beChecked(benutzerPage.getUserCheckbox());
notBeEnabled(benutzerPage.getLoeschenCheckbox());
notBeEnabled(benutzerPage.getPostCheckbox());
benutzerPage.clickUserCheckbox();
notBeChecked(benutzerPage.getUserCheckbox());
beEnabled(benutzerPage.getLoeschenCheckbox());
beEnabled(benutzerPage.getPostCheckbox());
});
it('should activate post checkbox and deactivate the other two checkboxes', () => {
benutzerPage.clickPostCheckbox();
beChecked(benutzerPage.getPostCheckbox());
notBeEnabled(benutzerPage.getLoeschenCheckbox());
notBeEnabled(benutzerPage.getUserCheckbox());
benutzerPage.clickPostCheckbox();
notBeChecked(benutzerPage.getPostCheckbox());
beEnabled(benutzerPage.getLoeschenCheckbox());
beEnabled(benutzerPage.getUserCheckbox());
});
}); });
import { PostfachE2EComponent } from '../../components/postfach/postfach.e2e.component'; import { PostfachE2EComponent } from '../../../components/postfach/postfach.e2e.component';
import { HeaderE2EComponent } from '../../page-objects/header.po'; import { HeaderE2EComponent } from '../../../page-objects/header.po';
import { MainPage, waitForSpinnerToDisappear } from '../../page-objects/main.po'; import { MainPage, waitForSpinnerToDisappear } from '../../../page-objects/main.po';
import { exist } from '../../support/cypress.util'; import { exist } from '../../../support/cypress.util';
import { loginAsAriane } from '../../support/user-util'; import { loginAsAriane } from '../../../support/user-util';
describe('Signatur', () => { describe('Signatur', () => {
const mainPage: MainPage = new MainPage(); const mainPage: MainPage = new MainPage();
...@@ -15,7 +15,7 @@ describe('Signatur', () => { ...@@ -15,7 +15,7 @@ describe('Signatur', () => {
loginAsAriane(); loginAsAriane();
}); });
it('should clear current signature0', () => { it('should clear current signature', () => {
waitForSpinnerToDisappear(); waitForSpinnerToDisappear();
exist(postfachTab.getSignaturText()); exist(postfachTab.getSignaturText());
......
ozgcloud:
bezeichner: admine2emain
environment: dev
e2eTest: true
project:
destinations:
- namespace: "*"
server: https://kubernetes.default.svc
administration:
enabled: true
admin_client:
enabled: true
ingress:
use_staging_cert: true
alfa:
env:
overrideSpringProfiles: "oc,e2e,dev"
ingress:
use_staging_cert: true
vorgang_manager:
env:
overrideSpringProfiles: "oc,e2e,dev"
user_manager:
ozgcloud:
usersync:
onstart: false
period: disabled
ingress:
use_staging_cert: true
smocker:
enabled: false
\ No newline at end of file
...@@ -4,8 +4,9 @@ import { HeaderE2EComponent } from './header.po'; ...@@ -4,8 +4,9 @@ import { HeaderE2EComponent } from './header.po';
export class MainPage { export class MainPage {
private readonly buildInfo: BuildInfoE2EComponent = new BuildInfoE2EComponent(); private readonly buildInfo: BuildInfoE2EComponent = new BuildInfoE2EComponent();
private readonly header: HeaderE2EComponent = new HeaderE2EComponent(); private readonly header: HeaderE2EComponent = new HeaderE2EComponent();
private readonly benutzerTab: string = 'nav-item-Benutzer__Rollen'; private readonly benutzerTab: string = 'caption-Benutzer__Rollen';
private readonly postfachTab: string = 'nav-item-Postfach'; private readonly postfachTab: string = 'caption-Postfach';
private readonly organisationseinheitenTab: string = 'caption-Organisationseinheiten';
public getBuildInfo(): BuildInfoE2EComponent { public getBuildInfo(): BuildInfoE2EComponent {
return this.buildInfo; return this.buildInfo;
...@@ -22,6 +23,14 @@ export class MainPage { ...@@ -22,6 +23,14 @@ export class MainPage {
public clickBenutzerTab(): void { public clickBenutzerTab(): void {
this.getBenutzerTab().click(); this.getBenutzerTab().click();
} }
public getOrganisationseinheitenTab(): Cypress.Chainable<Element> {
return cy.getTestElement(this.organisationseinheitenTab);
}
public clickOrganisationseinheitenTab(): void {
this.getOrganisationseinheitenTab().click();
}
} }
export function waitForSpinnerToDisappear(): boolean { export function waitForSpinnerToDisappear(): boolean {
......
...@@ -2,104 +2,104 @@ import { wait } from './cypress-helper'; ...@@ -2,104 +2,104 @@ import { wait } from './cypress-helper';
//TODO Naming der Methoden geradeziehen //TODO Naming der Methoden geradeziehen
export function containClass(element: any, cssClass: string): void { export function containClass(element: Cypress.Chainable<Element>, cssClass: string): void {
element.should('have.class', cssClass); element.should('have.class', cssClass);
} }
export function notContainClass(element: any, cssClass: string): void { export function notContainClass(element: Cypress.Chainable<Element>, cssClass: string): void {
element.should('not.have.class', cssClass); element.should('not.have.class', cssClass);
} }
export function exist(element: any): void { export function exist(element: Cypress.Chainable<Element>): void {
element.should('exist'); element.should('exist');
} }
export function notExist(element: any): void { export function notExist(element: Cypress.Chainable<Element>): void {
element.should('not.exist'); element.should('not.exist');
} }
export function haveText(element: any, text: string): void { export function haveText(element: Cypress.Chainable<Element>, text: string): void {
element element
.invoke('text') .invoke('text')
.then((elementText) => elementText.trim()) .then((elementText) => elementText.trim())
.should('equal', text); .should('equal', text);
} }
export function haveValue(element: any, value: string): void { export function haveValue(element: Cypress.Chainable<Element>, value: string): void {
element.should('have.value', value); element.should('have.value', value);
} }
export function haveFocus(element: any): void { export function haveFocus(element: Cypress.Chainable<Element>): void {
element.should('have.focus'); element.should('have.focus');
} }
export function mouseEnter(element: any): void { export function mouseEnter(element: Cypress.Chainable<Element>): void {
element.trigger('mouseenter'); element.trigger('mouseenter');
} }
export function mouseOver(element: any): void { export function mouseOver(element: Cypress.Chainable<Element>): void {
element.trigger('mouseover'); element.trigger('mouseover');
} }
export function contains(element: any, containing: string): void { export function contains(element: Cypress.Chainable<Element>, containing: string): void {
element.should('exist').contains(containing); element.should('exist').contains(containing);
} }
export function notContains(element: any, containing: string): void { export function notContains(element: Cypress.Chainable<Element>, containing: string): void {
element.contains(containing).should('not.exist'); element.contains(containing).should('not.exist');
} }
export function haveLength(element: any, length: number): void { export function haveLength(element: Cypress.Chainable<Element>, length: number): void {
element.should('have.length', length); element.should('have.length', length);
} }
export function beChecked(element: any): void { export function beChecked(element: Cypress.Chainable<Element>): void {
element.should('be.checked'); element.should('be.checked');
} }
export function notBeChecked(element: any): void { export function notBeChecked(element: Cypress.Chainable<Element>): void {
element.should('not.be.checked'); element.should('not.be.checked');
} }
export function beEnabled(element: Cypress.Chainable<Element>): void {
element.should('be.enabled');
}
export function notBeEnabled(element: Cypress.Chainable<Element>): void {
element.should('not.be.enabled');
}
//TODO: "first()" rausnehmen -> im html eine entprechende data-test-id ansprechen?! | trennen in "get" und "verify" //TODO: "first()" rausnehmen -> im html eine entprechende data-test-id ansprechen?! | trennen in "get" und "verify"
export function shouldFirstContains(element: any, containing: string) { export function shouldFirstContains(element: Cypress.Chainable<Element>, containing: string) {
element.first().should('exist').contains(containing); element.first().should('exist').contains(containing);
} }
export function shouldHaveAttributeBeGreaterThan( export function shouldHaveAttributeBeGreaterThan(element: Cypress.Chainable<Element>, attributeName: string, value: number) {
element: any,
attributeName: string,
value: number,
) {
element.first().should('exist').invoke(attributeName).should('be.gt', value); element.first().should('exist').invoke(attributeName).should('be.gt', value);
} }
export function shouldHaveAttributeBeLowerThan(element: any, attributeName: string, value: number) { export function shouldHaveAttributeBeLowerThan(element: Cypress.Chainable<Element>, attributeName: string, value: number) {
element.first().should('exist').invoke(attributeName).should('be.gt', value); element.first().should('exist').invoke(attributeName).should('be.gt', value);
} }
// //
export function shouldHaveAttribute(element: any, name: string, value: string) { export function shouldHaveAttribute(element: Cypress.Chainable<Element>, name: string, value: string) {
element.should('have.attr', name, value); element.should('have.attr', name, value);
} }
export function visible(element: any) { export function visible(element: Cypress.Chainable<Element>) {
element.should('be.visible'); element.should('be.visible');
} }
export function notBeVisible(element: any) { export function notBeVisible(element: Cypress.Chainable<Element>) {
element.should('not.be.visible'); element.should('not.be.visible');
} }
export function enter(element: any): void { export function enter(element: Cypress.Chainable<Element>): void {
element.clear().type(CypressKeyboardActions.ENTER); element.clear().type(CypressKeyboardActions.ENTER);
} }
export function enterWith( export function enterWith(element: Cypress.Chainable<JQuery<HTMLElement>>, value: string, delayBeforeEnter: number = 200): void {
element: Cypress.Chainable<JQuery<HTMLElement>>,
value: string,
delayBeforeEnter: number = 200,
): void {
element.clear().type(value); element.clear().type(value);
wait(delayBeforeEnter); wait(delayBeforeEnter);
element.type(CypressKeyboardActions.ENTER); element.type(CypressKeyboardActions.ENTER);
...@@ -109,7 +109,7 @@ export function typeText(element: Cypress.Chainable<JQuery<HTMLElement>>, value: ...@@ -109,7 +109,7 @@ export function typeText(element: Cypress.Chainable<JQuery<HTMLElement>>, value:
element.type(value); element.type(value);
} }
export function backspaceOn(element: any): void { export function backspaceOn(element: Cypress.Chainable<Element>): void {
element.type(CypressKeyboardActions.BACKSPACE); element.type(CypressKeyboardActions.BACKSPACE);
} }
......
...@@ -8,24 +8,22 @@ ...@@ -8,24 +8,22 @@
"targets": { "targets": {
"build": { "build": {
"executor": "@angular-devkit/build-angular:browser", "executor": "@angular-devkit/build-angular:browser",
"outputs": [ "outputs": ["{options.outputPath}"],
"{options.outputPath}"
],
"options": { "options": {
"outputPath": "dist/apps/admin", "outputPath": "dist/apps/admin",
"index": "apps/admin/src/index.html", "index": "apps/admin/src/index.html",
"main": "apps/admin/src/main.ts", "main": "apps/admin/src/main.ts",
"polyfills": [ "polyfills": ["zone.js"],
"zone.js"
],
"tsConfig": "apps/admin/tsconfig.app.json", "tsConfig": "apps/admin/tsconfig.app.json",
"assets": [ "assets": [
"apps/admin/src/favicon.ico", "apps/admin/src/assets",
"apps/admin/src/assets" {
], "input": "apps/admin/src/favicon",
"styles": [ "glob": "**/*",
"apps/admin/src/styles.scss" "output": ""
}
], ],
"styles": ["apps/admin/src/styles.scss"],
"scripts": [], "scripts": [],
"stylePreprocessorOptions": { "stylePreprocessorOptions": {
"includePaths": [ "includePaths": [
...@@ -87,15 +85,11 @@ ...@@ -87,15 +85,11 @@
}, },
"lint": { "lint": {
"executor": "@nx/eslint:lint", "executor": "@nx/eslint:lint",
"outputs": [ "outputs": ["{options.outputFile}"]
"{options.outputFile}"
]
}, },
"test": { "test": {
"executor": "@nx/jest:jest", "executor": "@nx/jest:jest",
"outputs": [ "outputs": ["{workspaceRoot}/coverage/{projectRoot}"],
"{workspaceRoot}/coverage/{projectRoot}"
],
"options": { "options": {
"jestConfig": "apps/admin/jest.config.ts" "jestConfig": "apps/admin/jest.config.ts"
} }
...@@ -108,20 +102,14 @@ ...@@ -108,20 +102,14 @@
}, },
"container": { "container": {
"executor": "@nx-tools/nx-container:build", "executor": "@nx-tools/nx-container:build",
"dependsOn": [ "dependsOn": ["build"],
"build"
],
"options": { "options": {
"engine": "docker", "engine": "docker",
"push": false, "push": false,
"metadata": { "metadata": {
"images": [ "images": ["docker.ozg-sh.de/admin-client"],
"docker.ozg-sh.de/admin-client"
],
"load": true, "load": true,
"tags": [ "tags": ["build-latest"]
"build-latest"
]
} }
} }
} }
......
<ng-container *ngIf="(apiRootStateResource$ | async)?.resource as apiRoot"> <ng-container *ngIf="(apiRootStateResource$ | async)?.resource as apiRoot">
<header <header
class="flex h-16 items-center justify-between border-b border-b-ozggray-300 bg-white px-9 py-2" class="z-50 flex h-16 items-center justify-between border-b border-b-ozggray-300 bg-white px-9 py-2"
data-test-id="admin-header" data-test-id="admin-header"
> >
<a <a
...@@ -37,6 +37,8 @@ ...@@ -37,6 +37,8 @@
<unavailable-page></unavailable-page> <unavailable-page></unavailable-page>
</ng-template> </ng-template>
</main> </main>
<section class="w-5">
<alfa-build-info *ngIf="apiRoot" [apiRoot]="apiRoot" data-test-id="build-info" />
</section>
</div> </div>
<footer data-test-id="build-version">Version: {{ apiRoot.version }}</footer>
</ng-container> </ng-container>
import { ApiRootLinkRel, ApiRootResource, ApiRootService } from '@alfa-client/api-root-shared'; import { ApiRootLinkRel, ApiRootResource, ApiRootService } from '@alfa-client/api-root-shared';
import { import { BuildInfoComponent } from '@alfa-client/common';
HasLinkPipe, import { HasLinkPipe, createEmptyStateResource, createStateResource } from '@alfa-client/tech-shared';
createEmptyStateResource,
createStateResource,
} from '@alfa-client/tech-shared';
import { import {
Mock, Mock,
dispatchEventFromFixture, dispatchEventFromFixture,
...@@ -13,7 +10,7 @@ import { ...@@ -13,7 +10,7 @@ import {
notExistsAsHtmlElement, notExistsAsHtmlElement,
} from '@alfa-client/test-utils'; } from '@alfa-client/test-utils';
import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ComponentFixture, TestBed } from '@angular/core/testing';
import { Router, RouterOutlet } from '@angular/router'; import { ActivatedRoute, Router, RouterOutlet } from '@angular/router';
import { import {
AdminLogoIconComponent, AdminLogoIconComponent,
MailboxIconComponent, MailboxIconComponent,
...@@ -36,7 +33,7 @@ describe('AppComponent', () => { ...@@ -36,7 +33,7 @@ describe('AppComponent', () => {
let fixture: ComponentFixture<AppComponent>; let fixture: ComponentFixture<AppComponent>;
const adminHeaderSelector: string = getDataTestIdOf('admin-header'); const adminHeaderSelector: string = getDataTestIdOf('admin-header');
const buildVersionSelector: string = getDataTestIdOf('build-version'); const buildInfoSelector: string = getDataTestIdOf('build-info');
const userProfileButtonSelector: string = getDataTestIdOf('user-profile-button'); const userProfileButtonSelector: string = getDataTestIdOf('user-profile-button');
const navigationSelector: string = getDataTestIdOf('navigation'); const navigationSelector: string = getDataTestIdOf('navigation');
const logoLink: string = getDataTestIdOf('logo-link'); const logoLink: string = getDataTestIdOf('logo-link');
...@@ -48,6 +45,18 @@ describe('AppComponent', () => { ...@@ -48,6 +45,18 @@ describe('AppComponent', () => {
}; };
const router: Mock<Router> = mock(Router); const router: Mock<Router> = mock(Router);
const route: Mock<ActivatedRoute> = {
...mock(ActivatedRoute),
snapshot: {
queryParams: {
iss: 'some-iss',
state: 'some-state',
session_state: 'some-session-state',
code: 'some-code',
},
} as any,
};
const apiRootService: Mock<ApiRootService> = mock(ApiRootService); const apiRootService: Mock<ApiRootService> = mock(ApiRootService);
beforeEach(async () => { beforeEach(async () => {
...@@ -62,6 +71,7 @@ describe('AppComponent', () => { ...@@ -62,6 +71,7 @@ describe('AppComponent', () => {
MockComponent(UnavailablePageComponent), MockComponent(UnavailablePageComponent),
MockComponent(NavbarComponent), MockComponent(NavbarComponent),
MockComponent(NavItemComponent), MockComponent(NavItemComponent),
MockComponent(BuildInfoComponent),
HasLinkPipe, HasLinkPipe,
MockDirective(RouterOutlet), MockDirective(RouterOutlet),
], ],
...@@ -78,6 +88,10 @@ describe('AppComponent', () => { ...@@ -78,6 +88,10 @@ describe('AppComponent', () => {
provide: Router, provide: Router,
useValue: router, useValue: router,
}, },
{
provide: ActivatedRoute,
useValue: route,
},
], ],
}).compileComponents(); }).compileComponents();
}); });
...@@ -87,6 +101,7 @@ describe('AppComponent', () => { ...@@ -87,6 +101,7 @@ describe('AppComponent', () => {
component = fixture.componentInstance; component = fixture.componentInstance;
}); });
describe('component', () => {
it(`should have as title 'admin'`, () => { it(`should have as title 'admin'`, () => {
const appTitle: string = fixture.componentInstance.title; const appTitle: string = fixture.componentInstance.title;
...@@ -108,6 +123,7 @@ describe('AppComponent', () => { ...@@ -108,6 +123,7 @@ describe('AppComponent', () => {
expect(component.doAfterLoggedIn).toHaveBeenCalled(); expect(component.doAfterLoggedIn).toHaveBeenCalled();
}); });
});
describe('do after logged in', () => { describe('do after logged in', () => {
it('should call apiRootService to getApiRoot', () => { it('should call apiRootService to getApiRoot', () => {
...@@ -116,14 +132,25 @@ describe('AppComponent', () => { ...@@ -116,14 +132,25 @@ describe('AppComponent', () => {
expect(apiRootService.getApiRoot).toHaveBeenCalled(); expect(apiRootService.getApiRoot).toHaveBeenCalled();
}); });
it('should navigate to default route', () => { it('should call forwardWithoutAuthenticationParams', () => {
component.forwardWithoutAuthenticationParams = jest.fn();
component.doAfterLoggedIn(); component.doAfterLoggedIn();
expect(router.navigate).toHaveBeenCalledWith(['/']); expect(component.forwardWithoutAuthenticationParams).toHaveBeenCalled();
});
});
describe('forward without authentication params', () => {
it('should navigate to same route without authentication params', () => {
component.forwardWithoutAuthenticationParams();
expect(router.navigate).toHaveBeenCalledWith([], { queryParams: {} });
}); });
}); });
}); });
describe('template', () => {
it('show not show header if apiRoot is not loaded', () => { it('show not show header if apiRoot is not loaded', () => {
component.apiRootStateResource$ = of(createEmptyStateResource<ApiRootResource>()); component.apiRootStateResource$ = of(createEmptyStateResource<ApiRootResource>());
...@@ -153,16 +180,13 @@ describe('AppComponent', () => { ...@@ -153,16 +180,13 @@ describe('AppComponent', () => {
it('should navigate to start page on click', () => { it('should navigate to start page on click', () => {
dispatchEventFromFixture(fixture, logoLink, 'click'); dispatchEventFromFixture(fixture, logoLink, 'click');
expect(router.navigate).toHaveBeenCalledWith(['/']); expect(router.navigate).toHaveBeenCalledWith([], { queryParams: {} });
}); });
}); });
describe('navigation', () => { describe('navigation', () => {
beforeEach(() => {});
it('should show links if configuration link exists', () => { it('should show links if configuration link exists', () => {
component.apiRootStateResource$ = of( component.apiRootStateResource$ = of(createStateResource(createApiRootResource([ApiRootLinkRel.CONFIGURATION])));
createStateResource(createApiRootResource([ApiRootLinkRel.CONFIGURATION])),
);
fixture.detectChanges(); fixture.detectChanges();
const navbarElement: HTMLElement = getElementFromFixture(fixture, navigationSelector); const navbarElement: HTMLElement = getElementFromFixture(fixture, navigationSelector);
...@@ -181,17 +205,15 @@ describe('AppComponent', () => { ...@@ -181,17 +205,15 @@ describe('AppComponent', () => {
}); });
describe('build version', () => { describe('build version', () => {
const apiResource: ApiRootResource = createApiRootResource(); it('should not be rendered if api root not loaded', () => {
notExistsAsHtmlElement(fixture, buildInfoSelector);
beforeEach(() => {
component.apiRootStateResource$ = of(createStateResource(apiResource));
}); });
it('should show after apiRoot loaded', () => { it('should show after apiRoot loaded', () => {
component.apiRootStateResource$ = of(createStateResource(createApiRootResource()));
fixture.detectChanges(); fixture.detectChanges();
const buildVersionElement = getElementFromFixture(fixture, buildVersionSelector); existsAsHtmlElement(fixture, buildInfoSelector);
expect(buildVersionElement.textContent.trim()).toEqual(`Version: ${apiResource.version}`);
}); });
}); });
...@@ -199,9 +221,7 @@ describe('AppComponent', () => { ...@@ -199,9 +221,7 @@ describe('AppComponent', () => {
beforeEach(() => {}); beforeEach(() => {});
it('should exist if configuration resource available', () => { it('should exist if configuration resource available', () => {
component.apiRootStateResource$ = of( component.apiRootStateResource$ = of(createStateResource(createApiRootResource([ApiRootLinkRel.CONFIGURATION])));
createStateResource(createApiRootResource([ApiRootLinkRel.CONFIGURATION])),
);
fixture.detectChanges(); fixture.detectChanges();
...@@ -217,3 +237,4 @@ describe('AppComponent', () => { ...@@ -217,3 +237,4 @@ describe('AppComponent', () => {
}); });
}); });
}); });
});
import { ApiRootLinkRel, ApiRootResource, ApiRootService } from '@alfa-client/api-root-shared'; import { ApiRootLinkRel, ApiRootResource, ApiRootService } from '@alfa-client/api-root-shared';
import { StateResource } from '@alfa-client/tech-shared'; import { StateResource } from '@alfa-client/tech-shared';
import { Component, OnInit } from '@angular/core'; import { Component, OnInit } from '@angular/core';
import { Router } from '@angular/router'; import { ActivatedRoute, Params, Router } from '@angular/router';
import { AuthenticationService } from 'libs/authentication/src/lib/authentication.service'; import { AuthenticationService } from 'libs/authentication/src/lib/authentication.service';
import { Observable } from 'rxjs'; import { Observable } from 'rxjs';
...@@ -19,6 +19,7 @@ export class AppComponent implements OnInit { ...@@ -19,6 +19,7 @@ export class AppComponent implements OnInit {
public authenticationService: AuthenticationService, public authenticationService: AuthenticationService,
private apiRootService: ApiRootService, private apiRootService: ApiRootService,
private router: Router, private router: Router,
private route: ActivatedRoute,
) {} ) {}
ngOnInit(): void { ngOnInit(): void {
...@@ -27,7 +28,17 @@ export class AppComponent implements OnInit { ...@@ -27,7 +28,17 @@ export class AppComponent implements OnInit {
doAfterLoggedIn(): void { doAfterLoggedIn(): void {
this.apiRootStateResource$ = this.apiRootService.getApiRoot(); this.apiRootStateResource$ = this.apiRootService.getApiRoot();
this.router.navigate(['/']); this.forwardWithoutAuthenticationParams();
}
forwardWithoutAuthenticationParams() {
const queryParams = this.getQueryParamsWithoutAuthentication();
this.router.navigate([], { queryParams });
}
private getQueryParamsWithoutAuthentication(): Params {
const { iss, state, session_state, code, ...queryParams } = this.route.snapshot.queryParams;
return queryParams;
} }
protected readonly ApiRootLinkRel = ApiRootLinkRel; protected readonly ApiRootLinkRel = ApiRootLinkRel;
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment