diff --git a/build.gradle.kts b/build.gradle.kts index 9e0320b82..335a11ff8 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -41,6 +41,13 @@ version = properties("pluginVersion").get() val remoteRobotVersion = "0.11.22" val okHttp3Version = "4.12.0" val kotestVersion = "5.8.1" +val retrofit2Vertion = "2.11.0" +val junitVersion = "5.10.2" +val mockkVersion = "1.13.10" +val ibmMqVersion = "9.3.5.0" +val jGraphTVersion = "1.5.2" +val zoweKotlinSdkVersion = "0.4.0" +val javaKeytarVersion = "1.0.0" repositories { mavenCentral() @@ -66,28 +73,33 @@ java { } dependencies { - implementation(group = "com.squareup.retrofit2", name = "retrofit", version = "2.9.0") - implementation("com.squareup.retrofit2:converter-gson:2.9.0") - implementation("com.squareup.retrofit2:converter-scalars:2.9.0") + implementation(group = "com.squareup.retrofit2", name = "retrofit", version = retrofit2Vertion) + implementation("com.squareup.retrofit2:converter-gson:$retrofit2Vertion") + implementation("com.squareup.retrofit2:converter-scalars:$retrofit2Vertion") implementation("com.squareup.okhttp3:okhttp:$okHttp3Version") - implementation("org.jgrapht:jgrapht-core:1.5.2") - implementation("com.starxg:java-keytar:1.0.0") - implementation("org.zowe.sdk:zowe-kotlin-sdk:0.4.0") - implementation("com.ibm.mq:com.ibm.mq.allclient:9.3.4.1") - testImplementation("io.mockk:mockk:1.13.9") - testImplementation("org.junit.jupiter:junit-jupiter-api:5.10.1") + implementation("org.jgrapht:jgrapht-core:$jGraphTVersion") + implementation("com.starxg:java-keytar:$javaKeytarVersion") + implementation("org.zowe.sdk:zowe-kotlin-sdk:$zoweKotlinSdkVersion") + implementation("com.ibm.mq:com.ibm.mq.allclient:$ibmMqVersion") + implementation("org.junit.jupiter:junit-jupiter-params:$junitVersion") + testImplementation("org.junit.jupiter:junit-jupiter-api:$junitVersion") + testImplementation("io.mockk:mockk:$mockkVersion") testImplementation("io.kotest:kotest-assertions-core:$kotestVersion") testImplementation("io.kotest:kotest-runner-junit5:$kotestVersion") testImplementation("com.intellij.remoterobot:remote-robot:$remoteRobotVersion") testImplementation("com.intellij.remoterobot:remote-fixtures:$remoteRobotVersion") testImplementation("com.squareup.okhttp3:mockwebserver:$okHttp3Version") testImplementation("com.squareup.okhttp3:okhttp-tls:$okHttp3Version") - testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.10.1") - testRuntimeOnly("org.junit.vintage:junit-vintage-engine:5.10.1") + testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:$junitVersion") + testRuntimeOnly("org.junit.vintage:junit-vintage-engine:$junitVersion") } intellij { version.set(properties("platformVersion").get()) + // !Development only! + // downloadSources.set(true) + // In Settings | Advanced Settings enable option Download sources in section Build Tools. Gradle. + // Then invoke Reload All Gradle Projects action from the Gradle tool window. } // Configure Gradle Changelog Plugin - read more: https://github.com/JetBrains/gradle-changelog-plugin diff --git a/src/main/kotlin/org/zowe/explorer/common/ui/Table.kt b/src/main/kotlin/org/zowe/explorer/common/ui/Table.kt index fde1f5d03..f69e110bd 100755 --- a/src/main/kotlin/org/zowe/explorer/common/ui/Table.kt +++ b/src/main/kotlin/org/zowe/explorer/common/ui/Table.kt @@ -14,9 +14,7 @@ import com.intellij.openapi.actionSystem.ActionToolbarPosition import com.intellij.openapi.actionSystem.AnActionEvent import com.intellij.ui.AnActionButton import com.intellij.ui.ToolbarDecorator -import com.intellij.ui.dsl.builder.Row -import com.intellij.ui.dsl.gridLayout.HorizontalAlign -import com.intellij.ui.dsl.gridLayout.VerticalAlign +import com.intellij.ui.dsl.builder.* import com.intellij.ui.table.TableView import javax.swing.JPanel import javax.swing.JTable @@ -152,8 +150,7 @@ fun Row.tableWithToolbar( .apply { toolbarTableBuilder() } .createPanel() return cell(tableComponent) - .horizontalAlign(HorizontalAlign.FILL) - .verticalAlign(VerticalAlign.FILL) + .align(AlignX.FILL.plus(AlignY.FILL)) } val TableModelEvent.rows: IntRange diff --git a/src/main/kotlin/org/zowe/explorer/common/ui/ValidatingTableView.kt b/src/main/kotlin/org/zowe/explorer/common/ui/ValidatingTableView.kt index ae61a4fb8..08fba233a 100755 --- a/src/main/kotlin/org/zowe/explorer/common/ui/ValidatingTableView.kt +++ b/src/main/kotlin/org/zowe/explorer/common/ui/ValidatingTableView.kt @@ -19,6 +19,11 @@ class ValidatingTableView( model: ValidatingListTableModel, val disposable: Disposable ) : TableView(model) { + init { + for (column in columnModel.columns) { + column.minWidth = 150 + } + } @Suppress("UNCHECKED_CAST") override fun getListTableModel(): ValidatingListTableModel { diff --git a/src/main/kotlin/org/zowe/explorer/config/connect/Credentials.kt b/src/main/kotlin/org/zowe/explorer/config/connect/Credentials.kt index 1dfac6a06..a6da199b7 100755 --- a/src/main/kotlin/org/zowe/explorer/config/connect/Credentials.kt +++ b/src/main/kotlin/org/zowe/explorer/config/connect/Credentials.kt @@ -38,7 +38,7 @@ class Credentials { if (other == null || javaClass != other.javaClass) return false val that = other as Credentials if (configUuid != that.configUuid) return false - return if (username != that.username) false else password == that.password + return username == that.username && password == that.password } override fun hashCode(): Int { @@ -51,9 +51,9 @@ class Credentials { override fun toString(): String { return "Credentials{" + - "connectionConfigUuid='" + configUuid + '\'' + - ", username='" + username + '\'' + - ", password='" + password + '\'' + - '}' + "connectionConfigUuid='" + configUuid + '\'' + + ", username='" + username + '\'' + + ", password='" + password + '\'' + + '}' } } diff --git a/src/main/kotlin/org/zowe/explorer/config/connect/ui/ChangePasswordDialog.kt b/src/main/kotlin/org/zowe/explorer/config/connect/ui/ChangePasswordDialog.kt index 3df121916..5e11fe420 100644 --- a/src/main/kotlin/org/zowe/explorer/config/connect/ui/ChangePasswordDialog.kt +++ b/src/main/kotlin/org/zowe/explorer/config/connect/ui/ChangePasswordDialog.kt @@ -13,7 +13,6 @@ package org.zowe.explorer.config.connect.ui import com.intellij.icons.AllIcons import com.intellij.openapi.project.Project import com.intellij.ui.dsl.builder.* -import com.intellij.ui.dsl.gridLayout.HorizontalAlign import org.zowe.explorer.common.ui.StatefulDialog import org.zowe.explorer.utils.validateForBlank import org.zowe.explorer.utils.validateForPassword @@ -46,7 +45,7 @@ class ChangePasswordDialog( it.text = it.text.trim() validateForBlank(it) } - .horizontalAlign(HorizontalAlign.FILL) + .align(AlignX.FILL) } row { label("Old password: ") @@ -58,7 +57,7 @@ class ChangePasswordDialog( addShowHidePasswordIcon(this) } .validationOnApply { validateForBlank(it) } - .horizontalAlign(HorizontalAlign.FILL) + .align(AlignX.FILL) } row { label("New password: ") @@ -75,7 +74,7 @@ class ChangePasswordDialog( }) } .validationOnApply { validateForBlank(it) } - .horizontalAlign(HorizontalAlign.FILL) + .align(AlignX.FILL) } row { label("Confirm password: ") @@ -87,7 +86,7 @@ class ChangePasswordDialog( addShowHidePasswordIcon(this) } .validationOnApply { validateForBlank(it) ?: validateForPassword(state.newPassword, it) } - .horizontalAlign(HorizontalAlign.FILL) + .align(AlignX.FILL) } } } diff --git a/src/main/kotlin/org/zowe/explorer/config/connect/ui/zosmf/ConnectionDialog.kt b/src/main/kotlin/org/zowe/explorer/config/connect/ui/zosmf/ConnectionDialog.kt index 62f0a57ef..e4ecb7f1d 100644 --- a/src/main/kotlin/org/zowe/explorer/config/connect/ui/zosmf/ConnectionDialog.kt +++ b/src/main/kotlin/org/zowe/explorer/config/connect/ui/zosmf/ConnectionDialog.kt @@ -12,15 +12,19 @@ package org.zowe.explorer.config.connect.ui.zosmf import com.intellij.icons.AllIcons import com.intellij.openapi.components.service +import com.intellij.openapi.progress.ProcessCanceledException import com.intellij.openapi.progress.runBackgroundableTask import com.intellij.openapi.project.Project import com.intellij.openapi.ui.MessageDialogBuilder import com.intellij.openapi.ui.MessageType +import com.intellij.openapi.ui.Messages import com.intellij.openapi.ui.popup.Balloon import com.intellij.openapi.ui.popup.JBPopupFactory import com.intellij.ui.awt.RelativePoint +import com.intellij.ui.components.JBCheckBox +import com.intellij.ui.components.JBTextField import com.intellij.ui.dsl.builder.* -import com.intellij.ui.dsl.gridLayout.HorizontalAlign +import org.zowe.explorer.common.message import org.zowe.explorer.common.ui.DialogMode import org.zowe.explorer.common.ui.StatefulDialog import org.zowe.explorer.common.ui.showUntilDone @@ -39,10 +43,7 @@ import org.zowe.kotlinsdk.annotations.ZVersion import java.awt.Component import java.awt.Point import java.util.* -import javax.swing.JCheckBox -import javax.swing.JComponent -import javax.swing.JPasswordField -import javax.swing.JTextField +import javax.swing.* /** Dialog to add a new connection */ class ConnectionDialog( @@ -62,6 +63,31 @@ class ConnectionDialog( .toDialogState(crudable) else ConnectionDialogState() companion object { + /** + * Check if only the connection name has changed in the connection dialog + */ + private fun isOnlyConnectionNameChanged(initialState: ConnectionDialogState, state: ConnectionDialogState): Boolean { + return initialState.connectionName != state.connectionName && + initialState.connectionUrl == state.connectionUrl && + initialState.username == state.username && + initialState.password == state.password && + initialState.isAllowSsl == state.isAllowSsl + } + + /** + * Companion function which takes the instance of ConnectionDialog and checks its current state after OK button is pressed. + * @param dialog + * @return returns true if there are violations found, if no violations were found then returns false as a result of validation + */ + private fun validateSecureConnectionUsage(dialog : ConnectionDialog) : Boolean { + val urlToCheck = dialog.urlTextField.text.trim().startsWith("http://", true) + val sslEnabledCheck = dialog.sslCheckbox.isSelected + if (urlToCheck || sslEnabledCheck) { + return dialog.showSelfSignedUsageWarningDialog(dialog.urlTextField, dialog.sslCheckbox) + } + return false + } + /** Show Test connection dialog and test the connection regarding the dialog state. * First the method checks whether connection succeeds for specified user/password. * If connection succeeds then the method automatically fill in z/OS version for this connection. @@ -74,12 +100,25 @@ class ConnectionDialog( project: Project? = null, initialState: ConnectionDialogState ): ConnectionDialogState? { + var connectionDialog = ConnectionDialog(crudable, initialState, project) + val initState = initialState.clone() return showUntilDone( initialState = initialState, - factory = { ConnectionDialog(crudable, initialState, project) }, + factory = { connectionDialog }, test = { state -> + + if (validateSecureConnectionUsage(connectionDialog)) { + state.connectionUrl = connectionDialog.urlTextField.text + state.isAllowSsl = connectionDialog.sslCheckbox.isSelected + connectionDialog = ConnectionDialog(crudable, state, project) + return@showUntilDone false + } + val newTestedConnConfig : ConnectionConfig if (initialState.mode == DialogMode.UPDATE) { + if (isOnlyConnectionNameChanged(initState, state)) { + return@showUntilDone true + } val newState = state.clone() newState.initEmptyUuids(crudable) newTestedConnConfig = ConnectionConfig(newState.connectionUuid, newState.connectionName, newState.connectionUrl, newState.isAllowSsl, newState.zVersion) @@ -101,6 +140,7 @@ class ConnectionDialog( runCatching { service().performOperation(InfoOperation(newTestedConnConfig), it) }.onSuccess { + state.owner = whoAmI(newTestedConnConfig) ?: "" val systemInfo = service().performOperation(ZOSInfoOperation(newTestedConnConfig)) state.zVersion = when (systemInfo.zosVersion) { "04.25.00" -> ZVersion.ZOS_2_2 @@ -120,12 +160,16 @@ class ConnectionDialog( if (throwable != null) { state.mode = DialogMode.UPDATE val confirmMessage = "Do you want to add it anyway?" - val tMessage = throwable.message?.let { - if (it.contains("Exception")) { - it.substring(it.lastIndexOf(":") + 2) - .replaceFirstChar { c -> if (c.isLowerCase()) c.titlecase(Locale.getDefault()) else c.toString() } - } else { - it + val tMessage = if (throwable is ProcessCanceledException) { + message("explorer.cancel.by.user.error") + } else { + throwable.message?.let { + if (it.contains("Exception")) { + it.substring(it.lastIndexOf(":") + 2) + .replaceFirstChar { c -> if (c.isLowerCase()) c.titlecase(Locale.getDefault()) else c.toString() } + } else { + it + } } } val message = if (tMessage != null) { @@ -145,11 +189,9 @@ class ConnectionDialog( ask(project) } } + connectionDialog = ConnectionDialog(crudable, state, project) addAnyway } else { - runTask(title = "Retrieving user information", project = project) { - state.owner = whoAmI(newTestedConnConfig) ?: "" - } true } } @@ -188,12 +230,12 @@ class ConnectionDialog( ) } .focused() - .horizontalAlign(HorizontalAlign.FILL) + .align(AlignX.FILL) } else { textField() .bindText(state::connectionName) .applyToComponent { isEditable = false } - .horizontalAlign(HorizontalAlign.FILL) + .align(AlignX.FILL) } } row { @@ -206,7 +248,7 @@ class ConnectionDialog( validateForBlank(it) ?: validateZosmfUrl(it) } .also { urlTextField = it.component } - .horizontalAlign(HorizontalAlign.FILL) + .align(AlignX.FILL) } row { label("Username") @@ -224,7 +266,7 @@ class ConnectionDialog( .onApply { state.username = state.username.trim().uppercase() } - .horizontalAlign(HorizontalAlign.FILL) + .align(AlignX.FILL) } row { label("Password: ") @@ -232,13 +274,22 @@ class ConnectionDialog( passField = cell(JPasswordField()) .bindText(state::password) .validationOnApply { validateForBlank(it) } - .horizontalAlign(HorizontalAlign.FILL) + .align(AlignX.FILL) } indent { row { checkBox("Accept self-signed SSL certificates") .bindSelected(state::isAllowSsl) - .also { sslCheckbox = it.component } + .also { + it.component.apply { + addActionListener { + if (this.isSelected) { + showSelfSignedUsageWarningDialog(this) + } + } + sslCheckbox = this + } + } } } if (state.mode == DialogMode.UPDATE) { @@ -376,4 +427,40 @@ class ConnectionDialog( } } + /** + * Function shows the warning dialog if any violations found and resolves them in case "Back to safety" was clicked + * @param components - dialog components which have to be resolved to valid values + * @return result of the pressed button + */ + private fun showSelfSignedUsageWarningDialog(vararg components : Component) : Boolean { + // default return backToSafety + val backToSafety = true + val choice = Messages.showDialog( + project, + "Creating an unsecure connection (HTTP instead of HTTP(s) and/or using self-signed certificates) is not recommended.\n" + + "You do this at your own peril and risk, and we do not bear any responsibility for the possible consequences of using this type of connection.\n" + + "Please contact your system administrator to configure your system to be able to create a secure connection.\n\n" + + "Do you want to proceed anyway?", + "Attempt to create an unsecured connection", + arrayOf( + "Back to safety", + "Proceed" + ), + 0, + AllIcons.General.WarningDialog, + null + ) + return when (choice) { + 0 -> { + components.forEach { + if (it is JBCheckBox) it.isSelected = false + if (it is JBTextField) it.text = it.text.replace("http", "https", true) + } + backToSafety + } + 1 -> !backToSafety + else -> backToSafety + } + } + } diff --git a/src/main/kotlin/org/zowe/explorer/config/ws/ui/UrlColumn.kt b/src/main/kotlin/org/zowe/explorer/config/ws/ui/UrlColumn.kt index 7946b3968..856d515f5 100644 --- a/src/main/kotlin/org/zowe/explorer/config/ws/ui/UrlColumn.kt +++ b/src/main/kotlin/org/zowe/explorer/config/ws/ui/UrlColumn.kt @@ -43,14 +43,6 @@ class UrlColumn( return false } - /** Returns width of url column - * @param table column of table in GUI - * @return width of column in pixels - */ - override fun getWidth(table: JTable?): Int { - return 270 - } - /** * Returns instance of renderer object * @param item instance of working set config diff --git a/src/main/kotlin/org/zowe/explorer/config/ws/ui/WSNameColumn.kt b/src/main/kotlin/org/zowe/explorer/config/ws/ui/WSNameColumn.kt index 0a667e4d1..0ea0287a2 100644 --- a/src/main/kotlin/org/zowe/explorer/config/ws/ui/WSNameColumn.kt +++ b/src/main/kotlin/org/zowe/explorer/config/ws/ui/WSNameColumn.kt @@ -61,10 +61,6 @@ class WSNameColumn(private val wsProvider: () -> Li return false } - override fun getWidth(table: JTable?): Int { - return 200 - } - /** * Overloaded setter method. Sets a new name in the working set config. * @param item working set config [WorkingSetConfig]. diff --git a/src/main/kotlin/org/zowe/explorer/dataops/BatchedRemoteQuery.kt b/src/main/kotlin/org/zowe/explorer/dataops/BatchedRemoteQuery.kt index e4981252a..ffac6f75b 100644 --- a/src/main/kotlin/org/zowe/explorer/dataops/BatchedRemoteQuery.kt +++ b/src/main/kotlin/org/zowe/explorer/dataops/BatchedRemoteQuery.kt @@ -11,6 +11,10 @@ package org.zowe.explorer.dataops import org.zowe.explorer.config.connect.ConnectionConfig +import org.zowe.explorer.config.ws.DSMask +import org.zowe.explorer.dataops.fetch.LibraryQuery +import org.zowe.explorer.dataops.fetch.UssQuery +import org.zowe.explorer.dataops.sort.SortQueryKeys import org.zowe.explorer.utils.UNIT_CLASS /** @@ -30,10 +34,17 @@ class BatchedRemoteQuery( var alreadyFetched: Int = 0, var start: String? = null, var fetchNeeded: Boolean = true -): RemoteQuery { +): RemoteQuery, SortableQuery { override val resultClass: Class get() = UNIT_CLASS + override val sortKeys: List + get() = when(request) { + is DSMask -> mutableListOf(SortQueryKeys.DATASET_MODIFICATION_DATE, SortQueryKeys.ASCENDING) + is LibraryQuery -> mutableListOf(SortQueryKeys.MEMBER_MODIFICATION_DATE, SortQueryKeys.ASCENDING) + else -> mutableListOf() + } + /** * Sets default values for params that identify current state of fetching. */ diff --git a/src/main/kotlin/org/zowe/explorer/dataops/SortableQuery.kt b/src/main/kotlin/org/zowe/explorer/dataops/SortableQuery.kt new file mode 100644 index 000000000..da8b5aa38 --- /dev/null +++ b/src/main/kotlin/org/zowe/explorer/dataops/SortableQuery.kt @@ -0,0 +1,20 @@ +/* + * This program and the accompanying materials are made available under the terms of the + * Eclipse Public License v2.0 which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-v20.html + * + * SPDX-License-Identifier: EPL-2.0 + * + * Copyright IBA Group 2020 + */ + +package org.zowe.explorer.dataops + +import org.zowe.explorer.dataops.sort.SortQueryKeys + +/** + * Interface identifies what query is SortableQuery + */ +interface SortableQuery { + val sortKeys : List +} diff --git a/src/main/kotlin/org/zowe/explorer/dataops/UnitRemoteQueryImpl.kt b/src/main/kotlin/org/zowe/explorer/dataops/UnitRemoteQueryImpl.kt index fe017933c..dd3db16d2 100644 --- a/src/main/kotlin/org/zowe/explorer/dataops/UnitRemoteQueryImpl.kt +++ b/src/main/kotlin/org/zowe/explorer/dataops/UnitRemoteQueryImpl.kt @@ -11,6 +11,10 @@ package org.zowe.explorer.dataops import org.zowe.explorer.config.connect.ConnectionConfigBase +import org.zowe.explorer.config.ws.JobsFilter +import org.zowe.explorer.dataops.fetch.UssQuery +import org.zowe.explorer.dataops.sort.SortQueryKeys +import org.zowe.explorer.explorer.ui.UssNode import org.zowe.explorer.utils.UNIT_CLASS /** @@ -18,8 +22,14 @@ import org.zowe.explorer.utils.UNIT_CLASS */ data class UnitRemoteQueryImpl( override val request: R, - override val connectionConfig: Connection -) : RemoteQuery { + override val connectionConfig: Connection, +) : RemoteQuery, SortableQuery { override val resultClass: Class get() = UNIT_CLASS + override val sortKeys: List + get() = when(request) { + is UssQuery -> mutableListOf(SortQueryKeys.FILE_MODIFICATION_DATE, SortQueryKeys.ASCENDING) + is JobsFilter -> mutableListOf(SortQueryKeys.JOB_CREATION_DATE, SortQueryKeys.ASCENDING) + else -> mutableListOf() + } } diff --git a/src/main/kotlin/org/zowe/explorer/dataops/content/synchronizer/DocumentedSyncProvider.kt b/src/main/kotlin/org/zowe/explorer/dataops/content/synchronizer/DocumentedSyncProvider.kt index 0a588a0b8..c5bdbd301 100644 --- a/src/main/kotlin/org/zowe/explorer/dataops/content/synchronizer/DocumentedSyncProvider.kt +++ b/src/main/kotlin/org/zowe/explorer/dataops/content/synchronizer/DocumentedSyncProvider.kt @@ -11,18 +11,22 @@ package org.zowe.explorer.dataops.content.synchronizer import com.intellij.notification.Notification +import com.intellij.notification.NotificationAction import com.intellij.notification.NotificationType import com.intellij.notification.Notifications +import com.intellij.openapi.actionSystem.AnActionEvent import com.intellij.openapi.components.service import com.intellij.openapi.editor.Document import com.intellij.openapi.editor.impl.DocumentImpl import com.intellij.openapi.fileEditor.FileDocumentManager +import com.intellij.openapi.ui.Messages import com.intellij.openapi.util.text.StringUtil import com.intellij.openapi.util.text.StringUtilRt import com.intellij.openapi.vfs.VirtualFile import com.intellij.util.LineSeparator import org.zowe.explorer.dataops.DataOpsManager import org.zowe.explorer.dataops.attributes.RemoteUssAttributes +import org.zowe.explorer.dataops.exceptions.CallException import org.zowe.explorer.utils.castOrNull import org.zowe.explorer.utils.changeEncodingTo import org.zowe.explorer.vfs.MFVirtualFile @@ -51,15 +55,39 @@ class DocumentedSyncProvider( * param file - the file to get the name to display * param th - the throwable to show the error message */ - val defaultOnThrowableHandler: (VirtualFile, Throwable) -> Unit = { file, th -> - Notifications.Bus.notify( - Notification( - SYNC_NOTIFICATION_GROUP_ID, - "Cannot synchronize file \"${file.name}\" with mainframe", - th.message ?: "", - NotificationType.ERROR - ) - ) + val defaultOnThrowableHandler: (VirtualFile, Throwable) -> Unit = { _, th -> + lateinit var title: String + lateinit var details: String + + if (th is CallException) { + title = (th.errorParams?.getOrDefault("message", th.headMessage) as String).replaceFirstChar { it.uppercase() } + if (title.contains(".")) { + title = title.split(".")[0] + } + details = th.errorParams["details"]?.castOrNull>()?.joinToString("\n") ?: "Unknown error" + if (details.contains(":")) { + details = details.split(":").last() + } + } else { + title = th.message ?: th.toString() + details = "Unknown error" + } + Notification( + SYNC_NOTIFICATION_GROUP_ID, + title, + details, + NotificationType.ERROR + ).addAction(object : NotificationAction("More") { + override fun actionPerformed(e: AnActionEvent, notification: Notification) { + Messages.showErrorDialog( + e.project, + th.message ?: th.toString(), + title + ) + } + }).let { + Notifications.Bus.notify(it) + } } /** Default sync success handler. Won't do anything after the sync action is completed until redefined */ diff --git a/src/main/kotlin/org/zowe/explorer/dataops/exceptions/CallException.kt b/src/main/kotlin/org/zowe/explorer/dataops/exceptions/CallException.kt index 75aeb85c7..08b07ccdf 100644 --- a/src/main/kotlin/org/zowe/explorer/dataops/exceptions/CallException.kt +++ b/src/main/kotlin/org/zowe/explorer/dataops/exceptions/CallException.kt @@ -49,7 +49,7 @@ private fun formatMessage(code: Int, message: String, errorParams: Map<*, *>?): */ class CallException( val code: Int, - headMessage: String, + val headMessage: String, val errorParams: Map<*, *>? = null, override val cause: Throwable? = null ) : Exception(formatMessage(code, headMessage, errorParams)) diff --git a/src/main/kotlin/org/zowe/explorer/dataops/fetch/FileFetchProvider.kt b/src/main/kotlin/org/zowe/explorer/dataops/fetch/FileFetchProvider.kt index 920a56cfe..5c0dcbf80 100644 --- a/src/main/kotlin/org/zowe/explorer/dataops/fetch/FileFetchProvider.kt +++ b/src/main/kotlin/org/zowe/explorer/dataops/fetch/FileFetchProvider.kt @@ -10,12 +10,14 @@ package org.zowe.explorer.dataops.fetch +import com.intellij.ide.util.treeView.AbstractTreeNode import com.intellij.openapi.extensions.ExtensionPointName import com.intellij.openapi.progress.DumbProgressIndicator import com.intellij.openapi.progress.ProgressIndicator import com.intellij.openapi.vfs.VirtualFile import com.intellij.util.messages.Topic import org.zowe.explorer.dataops.Query +import java.time.LocalDateTime /** Interface that represents the file fetch provider and operations available for it */ interface FileFetchProvider, File : VirtualFile> { @@ -41,6 +43,25 @@ interface FileFetchProvider, File : VirtualFile> { /** Function for "load more" nodes */ fun loadMode(query: Q, progressIndicator: ProgressIndicator = DumbProgressIndicator.INSTANCE) + /** + * Function adds (node,query) pair with @param lastRefresh into the corresponding fetch provider refreshCacheState map + * + * @param query + * @param node + * @param lastRefresh + * @return Void + */ + fun applyRefreshCacheDate(query: Q, node: AbstractTreeNode<*>, lastRefresh: LocalDateTime) + + /** + * Function finds the lastRefresh date by query in refreshCacheSate map and returns it. + * If date was not found then returns null + * + * @param query + * @return LocalDateTime instance or null + */ + fun findCacheRefreshDateIfPresent(query: Q): LocalDateTime? + /** * File fetch provider contains all list of queries inside. * If the query was created with default parameters - it will find query with real parameters. diff --git a/src/main/kotlin/org/zowe/explorer/dataops/fetch/JobFetchHelper.kt b/src/main/kotlin/org/zowe/explorer/dataops/fetch/JobFetchHelper.kt index d27ced2eb..355b7933b 100644 --- a/src/main/kotlin/org/zowe/explorer/dataops/fetch/JobFetchHelper.kt +++ b/src/main/kotlin/org/zowe/explorer/dataops/fetch/JobFetchHelper.kt @@ -42,10 +42,7 @@ class JobFetchHelper(private val query: RemoteQuery, Collection>() private val cacheState = mutableMapOf, CacheState>() - protected var errorMessages = mutableMapOf, String>() + private val refreshCacheState = mutableMapOf, RemoteQuery>, LocalDateTime>() + private var errorMessages = mutableMapOf, String>() /** * Returns successfully cached files. @@ -163,7 +165,8 @@ abstract class RemoteFileFetchProviderBase = getFetchedFiles(query, progressIndicator) // Cleans up attributes of invalid files cache[query] @@ -208,6 +211,13 @@ abstract class RemoteFileFetchProviderBase, node: AbstractTreeNode<*>, lastRefresh: LocalDateTime) { + lock.withLock { refreshCacheState.computeIfAbsent(Pair(node, query)) { _ -> lastRefresh } } + } + override fun findCacheRefreshDateIfPresent(query: RemoteQuery): LocalDateTime? { + return refreshCacheState.filter { it.key.second == query }.firstOrNull()?.value + } + /** * Clears the cache with sending the cache change topic. * @param query query that identifies the cache. @@ -231,6 +241,7 @@ abstract class RemoteFileFetchProviderBase, sendTopic: Boolean) { cacheState.remove(query) if (sendTopic) { + lock.withLock { refreshCacheState.keys.removeIf { it.second == query } } sendTopic(FileFetchProvider.CACHE_CHANGES, dataOpsManager.componentManager).onCacheCleaned(query) } } diff --git a/src/main/kotlin/org/zowe/explorer/dataops/sort/SortQueryKeys.kt b/src/main/kotlin/org/zowe/explorer/dataops/sort/SortQueryKeys.kt new file mode 100644 index 000000000..19b6349ec --- /dev/null +++ b/src/main/kotlin/org/zowe/explorer/dataops/sort/SortQueryKeys.kt @@ -0,0 +1,46 @@ +/* + * This program and the accompanying materials are made available under the terms of the + * Eclipse Public License v2.0 which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-v20.html + * + * SPDX-License-Identifier: EPL-2.0 + * + * Copyright IBA Group 2020 + */ +package org.zowe.explorer.dataops.sort + +/** + * Enum class represents the sorting keys which is currently enabled for particular Node + */ +enum class SortQueryKeys(private val sortType: String) { + FILE_NAME("uss_file_name"), + FILE_TYPE("uss_type"), + FILE_MODIFICATION_DATE("uss_modification_date"), + DATASET_NAME("dataset_name"), + DATASET_TYPE("dataset_type"), + DATASET_MODIFICATION_DATE("dataset_modification_date"), + MEMBER_NAME("member_name"), + MEMBER_MODIFICATION_DATE("member_modification_date"), + JOB_NAME("Job Name"), + JOB_CREATION_DATE("Job Creation Date"), + JOB_COMPLETION_DATE("Job Completion Date"), + JOB_STATUS("Job Status"), + JOB_OWNER("Job Owner"), + JOB_ID("Job ID"), + ASCENDING("Ascending"), + DESCENDING("Descending"); + + override fun toString(): String { + return sortType + } +} + +val typedSortKeys : List by lazy { + return@lazy listOf(SortQueryKeys.FILE_NAME, SortQueryKeys.FILE_TYPE, SortQueryKeys.FILE_MODIFICATION_DATE, SortQueryKeys.JOB_NAME, + SortQueryKeys.JOB_ID, SortQueryKeys.JOB_OWNER, SortQueryKeys.JOB_STATUS, SortQueryKeys.JOB_CREATION_DATE, SortQueryKeys.JOB_COMPLETION_DATE, + SortQueryKeys.DATASET_NAME, SortQueryKeys.DATASET_TYPE, SortQueryKeys.DATASET_MODIFICATION_DATE, SortQueryKeys.MEMBER_NAME, SortQueryKeys.MEMBER_MODIFICATION_DATE) +} + +val orderingSortKeys : List by lazy { + return@lazy listOf(SortQueryKeys.ASCENDING, SortQueryKeys.DESCENDING) +} diff --git a/src/main/kotlin/org/zowe/explorer/editor/FileEditorEventsListener.kt b/src/main/kotlin/org/zowe/explorer/editor/FileEditorEventsListener.kt index 39f14913e..fbaa81b6c 100644 --- a/src/main/kotlin/org/zowe/explorer/editor/FileEditorEventsListener.kt +++ b/src/main/kotlin/org/zowe/explorer/editor/FileEditorEventsListener.kt @@ -10,7 +10,6 @@ package org.zowe.explorer.editor -import com.intellij.openapi.actionSystem.ex.ActionUtil import com.intellij.openapi.components.service import com.intellij.openapi.editor.ex.EditorEx import com.intellij.openapi.fileEditor.FileEditorManager @@ -19,8 +18,14 @@ import com.intellij.openapi.progress.runModalTask import com.intellij.openapi.vfs.VirtualFile import org.zowe.explorer.config.ConfigService import org.zowe.explorer.dataops.DataOpsManager -import org.zowe.explorer.dataops.content.synchronizer.* -import org.zowe.explorer.utils.* +import org.zowe.explorer.dataops.content.synchronizer.AutoSyncFileListener +import org.zowe.explorer.dataops.content.synchronizer.DocumentedSyncProvider +import org.zowe.explorer.dataops.content.synchronizer.SaveStrategy +import org.zowe.explorer.utils.checkEncodingCompatibility +import org.zowe.explorer.utils.runReadActionInEdtAndWait +import org.zowe.explorer.utils.runWriteActionInEdtAndWait +import org.zowe.explorer.utils.sendTopic +import org.zowe.explorer.utils.showSaveAnywayDialog import org.zowe.explorer.vfs.MFVirtualFile /** diff --git a/src/main/kotlin/org/zowe/explorer/editor/MFPastePreprocessor.kt b/src/main/kotlin/org/zowe/explorer/editor/MFPastePreprocessor.kt index 0d68acc28..cb6f1a1be 100644 --- a/src/main/kotlin/org/zowe/explorer/editor/MFPastePreprocessor.kt +++ b/src/main/kotlin/org/zowe/explorer/editor/MFPastePreprocessor.kt @@ -20,7 +20,6 @@ import com.intellij.openapi.util.Ref import com.intellij.psi.PsiFile import org.zowe.explorer.utils.`is` import org.zowe.explorer.vfs.MFVirtualFile -import org.jetbrains.annotations.ApiStatus import java.awt.datatransfer.DataFlavor import java.awt.datatransfer.Transferable diff --git a/src/main/kotlin/org/zowe/explorer/explorer/Explorer.kt b/src/main/kotlin/org/zowe/explorer/explorer/Explorer.kt index c512aaef8..1b7141f57 100755 --- a/src/main/kotlin/org/zowe/explorer/explorer/Explorer.kt +++ b/src/main/kotlin/org/zowe/explorer/explorer/Explorer.kt @@ -12,6 +12,7 @@ package org.zowe.explorer.explorer import com.intellij.ide.BrowserUtil import com.intellij.notification.Notification +import com.intellij.notification.NotificationAction import com.intellij.notification.NotificationType import com.intellij.notification.Notifications import com.intellij.openapi.Disposable @@ -24,8 +25,10 @@ import com.intellij.openapi.extensions.ExtensionPointName import com.intellij.openapi.progress.ProcessCanceledException import com.intellij.openapi.project.DumbAwareAction import com.intellij.openapi.project.Project +import com.intellij.openapi.ui.Messages import com.intellij.openapi.util.Disposer import com.intellij.util.messages.Topic +import org.zowe.explorer.common.message import org.zowe.explorer.config.CONFIGS_CHANGED import org.zowe.explorer.config.configCrudable import org.zowe.explorer.config.connect.CREDENTIALS_CHANGED @@ -33,6 +36,7 @@ import org.zowe.explorer.config.connect.ConnectionConfig import org.zowe.explorer.config.connect.ConnectionConfigBase import org.zowe.explorer.config.connect.CredentialsListener import org.zowe.explorer.dataops.DataOpsManager +import org.zowe.explorer.dataops.exceptions.CallException import org.zowe.explorer.editor.ChangeContentService import org.zowe.explorer.utils.* import org.zowe.explorer.utils.crudable.EntityWithUuid @@ -193,15 +197,50 @@ abstract class AbstractExplorerBase>()?.joinToString("\n") ?: "Unknown error" + if (details.contains(":")) { + details = details.split(":").last() + } + } else { + title = t.message ?: t.toString() + details = "Unknown error" } + Notification( EXPLORER_NOTIFICATION_GROUP_ID, - "Error in plugin Zowe Explorer", - t.message ?: t.toString(), + title, + details, NotificationType.ERROR - ).let { + ).addAction(object : NotificationAction("More") { + override fun actionPerformed(e: AnActionEvent, notification: Notification) { + if (t is ProcessCanceledException || t.cause is ProcessCanceledException) { + Messages.showErrorDialog( + project, + details + "\n\n" + (t.message ?: t.toString()), + title + ) + } else { + Messages.showErrorDialog( + project, + t.message ?: t.toString(), + title + ) + } + } + }).let { Notifications.Bus.notify(it) } } diff --git a/src/main/kotlin/org/zowe/explorer/explorer/actions/AllocateActionBase.kt b/src/main/kotlin/org/zowe/explorer/explorer/actions/AllocateActionBase.kt index da060529d..551d54847 100644 --- a/src/main/kotlin/org/zowe/explorer/explorer/actions/AllocateActionBase.kt +++ b/src/main/kotlin/org/zowe/explorer/explorer/actions/AllocateActionBase.kt @@ -112,6 +112,7 @@ abstract class AllocateActionBase : AnAction() { protected fun doAllocateAction(e: AnActionEvent, datasetInfo: Dataset = Dataset()) { val view = e.getExplorerView() ?: return val parentNode = view.mySelectedNodesData[0].node + val project = e.project if (parentNode is ExplorerUnitTreeNodeBase<*, *, *> && parentNode.unit is FilesWorkingSet) { val workingSet = parentNode.unit val explorer = workingSet.explorer @@ -120,13 +121,13 @@ abstract class AllocateActionBase : AnAction() { val initialState = preProcessState(datasetInfo) showUntilDone( initialState, - { initState -> AllocationDialog(project = e.project, config, initState) } + { initState -> AllocationDialog(project = project, config, initState) } ) { val state = postProcessState(it) var res = false runModalTask( title = "Allocating Data Set ${state.datasetName}", - project = e.project, + project = project, cancellable = true ) { progressIndicator -> runCatching { @@ -150,7 +151,7 @@ abstract class AllocateActionBase : AnAction() { showNotification(state, workingSet) } .onFailure { t -> - explorer.reportThrowable(t, e.project) + explorer.reportThrowable(t, project) initialState.errorMessage = t.message ?: t.toString() } } diff --git a/src/main/kotlin/org/zowe/explorer/explorer/actions/CreateUssEntityAction.kt b/src/main/kotlin/org/zowe/explorer/explorer/actions/CreateUssEntityAction.kt index 8590d7bd9..3c52e078d 100644 --- a/src/main/kotlin/org/zowe/explorer/explorer/actions/CreateUssEntityAction.kt +++ b/src/main/kotlin/org/zowe/explorer/explorer/actions/CreateUssEntityAction.kt @@ -69,6 +69,7 @@ abstract class CreateUssEntityAction : AnAction() { val view = e.getExplorerView() ?: return val selected = view.mySelectedNodesData[0] val selectedNode = selected.node + val project = e.project val node = if (selectedNode is UssFileNode) { selectedNode.parent?.takeIf { it is UssDirNode } } else { @@ -88,7 +89,7 @@ abstract class CreateUssEntityAction : AnAction() { if (filePath != null) { showUntilDone( initialState = fileType.apply { path = filePath }, - { initState -> CreateFileDialog(e.project, state = initState, filePath = filePath) } + { initState -> CreateFileDialog(project, state = initState, filePath = filePath) } ) { var res = false val allocationParams = it.toAllocationParams() @@ -99,7 +100,7 @@ abstract class CreateUssEntityAction : AnAction() { } runModalTask( title = "Creating $fileType ${allocationParams.fileName}", - project = e.project, + project = project, cancellable = true ) { indicator -> val ussDirNode = node.castOrNull() @@ -124,7 +125,7 @@ abstract class CreateUssEntityAction : AnAction() { ussDirNode?.cleanCache(false) res = true }.onFailure { t -> - view.explorer.reportThrowable(t, e.project) + view.explorer.reportThrowable(t, project) } } res diff --git a/src/main/kotlin/org/zowe/explorer/explorer/actions/EditJclAction.kt b/src/main/kotlin/org/zowe/explorer/explorer/actions/EditJclAction.kt index 98fe2c52b..11deeb175 100644 --- a/src/main/kotlin/org/zowe/explorer/explorer/actions/EditJclAction.kt +++ b/src/main/kotlin/org/zowe/explorer/explorer/actions/EditJclAction.kt @@ -50,6 +50,7 @@ class EditJclAction : AnAction() { val view = e.getExplorerView() ?: return val selected = view.mySelectedNodesData.getOrNull(0) val node = selected?.node + val project = e.project if (node is JobNode) { val connectionConfig = node.unit.connectionConfig if (connectionConfig != null) { @@ -57,7 +58,7 @@ class EditJclAction : AnAction() { val dataOpsManager = service() runBackgroundableTask( title = "Get JCL records for job ${attributes.jobInfo.jobName}", - project = e.project, + project = project, cancellable = true ) { runCatching { @@ -81,10 +82,10 @@ class EditJclAction : AnAction() { cachedFile = createdFile wasCreatedBefore = false } - val descriptor = e.project?.let { pr -> OpenFileDescriptor(pr, cachedFile) } + val descriptor = project?.let { pr -> OpenFileDescriptor(pr, cachedFile) } descriptor?.let { val syncProvider = - DocumentedSyncProvider(file = cachedFile, saveStrategy = SaveStrategy.default(e.project)) + DocumentedSyncProvider(file = cachedFile, saveStrategy = SaveStrategy.default(project)) if (!wasCreatedBefore) { syncProvider.putInitialContent(jclContentBytes) } else { @@ -98,7 +99,7 @@ class EditJclAction : AnAction() { } } }.onFailure { - node.explorer.reportThrowable(it, e.project) + node.explorer.reportThrowable(it, project) } } } diff --git a/src/main/kotlin/org/zowe/explorer/explorer/actions/GetFilePropertiesAction.kt b/src/main/kotlin/org/zowe/explorer/explorer/actions/GetFilePropertiesAction.kt index f7371b50b..c1b8c794e 100644 --- a/src/main/kotlin/org/zowe/explorer/explorer/actions/GetFilePropertiesAction.kt +++ b/src/main/kotlin/org/zowe/explorer/explorer/actions/GetFilePropertiesAction.kt @@ -56,6 +56,7 @@ class GetFilePropertiesAction : AnAction() { override fun actionPerformed(e: AnActionEvent) { val view = e.getExplorerView() ?: return val node = view.mySelectedNodesData.getOrNull(0)?.node ?: return + val project = e.project if (node is ExplorerUnitTreeNodeBase>) { val virtualFile = node.virtualFile val connectionConfig = node.unit.connectionConfig ?: return @@ -63,7 +64,7 @@ class GetFilePropertiesAction : AnAction() { val dataOpsManager = node.explorer.componentManager.service() when (val attributes = dataOpsManager.tryToGetAttributes(virtualFile)) { is RemoteDatasetAttributes -> { - val dialog = DatasetPropertiesDialog(e.project, DatasetState(attributes)) + val dialog = DatasetPropertiesDialog(project, DatasetState(attributes)) dialog.showAndGet() } @@ -76,12 +77,12 @@ class GetFilePropertiesAction : AnAction() { // } val oldCharset = attributes.charset val initFileMode = attributes.fileMode?.clone() - val dialog = UssFilePropertiesDialog(e.project, UssFileState(attributes, virtualFile.isBeingEditingNow())) + val dialog = UssFilePropertiesDialog(project, UssFileState(attributes, virtualFile.isBeingEditingNow())) if (dialog.showAndGet()) { if (attributes.fileMode?.owner != initFileMode?.owner || attributes.fileMode?.group != initFileMode?.group || attributes.fileMode?.all != initFileMode?.all) { runBackgroundableTask( title = "Changing file mode on ${attributes.path}", - project = e.project, + project = project, cancellable = true ) { if (attributes.fileMode != null) { @@ -113,7 +114,7 @@ class GetFilePropertiesAction : AnAction() { } is RemoteMemberAttributes -> { - val dialog = MemberPropertiesDialog(e.project, MemberState(attributes)) + val dialog = MemberPropertiesDialog(project, MemberState(attributes)) dialog.showAndGet() } } diff --git a/src/main/kotlin/org/zowe/explorer/explorer/actions/MigrationActions.kt b/src/main/kotlin/org/zowe/explorer/explorer/actions/MigrationActions.kt index fa80c4a25..f44a4a5a6 100644 --- a/src/main/kotlin/org/zowe/explorer/explorer/actions/MigrationActions.kt +++ b/src/main/kotlin/org/zowe/explorer/explorer/actions/MigrationActions.kt @@ -72,6 +72,7 @@ class RecallAction : DumbAwareAction() { */ override fun actionPerformed(e: AnActionEvent) { val view = e.getExplorerView() + val project = e.project if (view != null) { val triples = view.mySelectedNodesData.mapNotNull { getRequestDataForNode(it.node) } val operations: List = triples.map { @@ -89,7 +90,7 @@ class RecallAction : DumbAwareAction() { ) } }.onFailure { - view.explorer.reportThrowable(it, e.project) + view.explorer.reportThrowable(it, project) } } makeUniqueCacheClean(view.mySelectedNodesData.map { it.node }) @@ -130,6 +131,7 @@ class MigrateAction : DumbAwareAction() { */ override fun actionPerformed(e: AnActionEvent) { val view = e.getExplorerView() + val project = e.project if (view != null) { val triples = view.mySelectedNodesData.mapNotNull { getRequestDataForNode(it.node) } val operations: List = triples.map { @@ -147,7 +149,7 @@ class MigrateAction : DumbAwareAction() { ) } }.onFailure { - view.explorer.reportThrowable(it, e.project) + view.explorer.reportThrowable(it, project) } } makeUniqueCacheClean(view.mySelectedNodesData.map { it.node }) diff --git a/src/main/kotlin/org/zowe/explorer/explorer/actions/PurgeJobAction.kt b/src/main/kotlin/org/zowe/explorer/explorer/actions/PurgeJobAction.kt index 66dae88c9..c7f1fdb68 100644 --- a/src/main/kotlin/org/zowe/explorer/explorer/actions/PurgeJobAction.kt +++ b/src/main/kotlin/org/zowe/explorer/explorer/actions/PurgeJobAction.kt @@ -1,11 +1,23 @@ +/* + * This program and the accompanying materials are made available under the terms of the + * Eclipse Public License v2.0 which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-v20.html + * + * SPDX-License-Identifier: EPL-2.0 + * + * Copyright IBA Group 2020 + */ + package org.zowe.explorer.explorer.actions import com.intellij.notification.NotificationType import com.intellij.openapi.actionSystem.ActionUpdateThread import com.intellij.openapi.actionSystem.AnAction import com.intellij.openapi.actionSystem.AnActionEvent +import com.intellij.openapi.application.runInEdt import com.intellij.openapi.components.service import com.intellij.openapi.progress.runBackgroundableTask +import com.intellij.openapi.progress.runModalTask import org.zowe.explorer.api.api import org.zowe.explorer.config.connect.ConnectionConfig import org.zowe.explorer.config.connect.authToken @@ -13,17 +25,18 @@ import org.zowe.explorer.dataops.DataOpsManager import org.zowe.explorer.dataops.attributes.RemoteJobAttributes import org.zowe.explorer.dataops.operations.jobs.BasicPurgeJobParams import org.zowe.explorer.dataops.operations.jobs.PurgeJobOperation -import org.zowe.explorer.explorer.ui.ExplorerTreeNode -import org.zowe.explorer.explorer.ui.FetchNode import org.zowe.explorer.explorer.ui.JesExplorerView import org.zowe.explorer.explorer.ui.JesFilterNode +import org.zowe.explorer.explorer.ui.JesWsNode import org.zowe.explorer.explorer.ui.JobNode +import org.zowe.explorer.explorer.ui.NodeData import org.zowe.explorer.explorer.ui.getExplorerView import org.zowe.explorer.ui.build.jobs.JOBS_LOG_VIEW import org.zowe.explorer.ui.build.jobs.JobBuildTreeView import org.zowe.kotlinsdk.ExecData import org.zowe.kotlinsdk.JESApi -import org.zowe.kotlinsdk.Job +import retrofit2.Response +import java.util.concurrent.ConcurrentHashMap /** An action to purge a job */ class PurgeJobAction : AnAction() { @@ -45,68 +58,37 @@ class PurgeJobAction : AnAction() { e.presentation.isEnabledAndVisible = false return } - var jobStatus: Job? = null - var connectionConfig: ConnectionConfig? = null if (view is JesExplorerView) { - val node = view.mySelectedNodesData.getOrNull(0)?.node - if (node is ExplorerTreeNode<*, *>) { - val virtualFile = node.virtualFile - if (virtualFile != null) { - val dataOpsManager = node.explorer.componentManager.service() - val attributes: RemoteJobAttributes = - dataOpsManager.tryToGetAttributes(virtualFile)?.clone() as RemoteJobAttributes - jobStatus = attributes.jobInfo - connectionConfig = attributes.requesters[0].connectionConfig - } - } + val jobNodesToPurge = view.mySelectedNodesData + val filtersToNodesMap = selectJobNodesByWSFilters(jobNodesToPurge) + runMassivePurgeAndRefreshByFilter(filtersToNodesMap, jobNodesToPurge, e, view) + } else if (view is JobBuildTreeView) { - jobStatus = view.getJobLogger().logFetcher.getCachedJobStatus() - connectionConfig = view.getConnectionConfig() - } - val dataOpsManager = service() - if (jobStatus != null && connectionConfig != null) { - runBackgroundableTask( - title = "Purging ${jobStatus.jobName}: ${jobStatus.jobId}", - project = e.project, - cancellable = true - ) { - runCatching { - dataOpsManager.performOperation( - operation = PurgeJobOperation( - request = BasicPurgeJobParams(jobStatus.jobName, jobStatus.jobId), - connectionConfig = connectionConfig - ), - progressIndicator = it - ) - }.onFailure { - if (view is JesExplorerView) { - view.explorer.showNotification( - "Error purging ${jobStatus.jobName}: ${jobStatus.jobId}", - "${it.message}", - NotificationType.ERROR, - e.project + val jobStatus = view.getJobLogger().logFetcher.getCachedJobStatus() + val connectionConfig: ConnectionConfig = view.getConnectionConfig() + val dataOpsManager = service() + if (jobStatus != null) { + runBackgroundableTask( + title = "Purging ${jobStatus.jobName}: ${jobStatus.jobId}", + project = e.project, + cancellable = true + ) { + runCatching { + dataOpsManager.performOperation( + operation = PurgeJobOperation( + request = BasicPurgeJobParams(jobStatus.jobName, jobStatus.jobId), + connectionConfig = connectionConfig + ), + progressIndicator = it ) - } else if (view is JobBuildTreeView) { + }.onFailure { view.showNotification( "Error purging ${jobStatus.jobName}: ${jobStatus.jobId}", "${it.message}", e.project, NotificationType.ERROR ) - } - }.onSuccess { - if (view is JesExplorerView) { - view.explorer.showNotification( - "${jobStatus.jobName}: ${jobStatus.jobId} has been purged", - "$it", - NotificationType.INFORMATION, - e.project - ) - val jobFilterNode = view.mySelectedNodesData.getOrNull(0)?.node?.parent - if (jobFilterNode is FetchNode) { - waitJobReleasedAndRefresh(jobFilterNode, jobStatus) - } - } else if (view is JobBuildTreeView) { + }.onSuccess { view.showNotification( "${jobStatus.jobName}: ${jobStatus.jobId} has been purged", "$it", @@ -120,14 +102,18 @@ class PurgeJobAction : AnAction() { } /** - * Function which checks if job is already purged from JES2. If not, repeats the scenario, cleans cache otherwise - * @param jobParentNode - parent node for particular job - * @param jobInfo - job info for particular job + * After performing the massive purge of the jobs scope (several filters) z/OSMF returns immediate success in the response, + * but actual purge was not performed yet on the mainframe which causes the problems during fetching the new job list during refresh. + * The purpose of this function is to perform the refresh on each filter only when actual purge was performed + * Note: This function performs refresh only on those filters from which purge action is requested + * @param jobsByFilterWaitingPurgeMap - concurrent map of the filter-to-jobs relationship waiting to purge * @return Void */ - private fun waitJobReleasedAndRefresh(jobParentNode: ExplorerTreeNode, jobInfo: Job) { - if (jobParentNode is JesFilterNode) { - val query = jobParentNode.query + private fun waitJobsReleasedAndRefresh(jobsByFilterWaitingPurgeMap: ConcurrentHashMap>>) { + val foundJobsWaitingInPurgeQueue: MutableList = mutableListOf() + val filtersWithRefreshErrors: MutableMap> = mutableMapOf() + jobsByFilterWaitingPurgeMap.keys.forEach { filterNode -> + val query = filterNode.query if (query != null) { val response = api(query.connectionConfig).getFilteredJobs( basicCredentials = query.connectionConfig.authToken, @@ -138,11 +124,175 @@ class PurgeJobAction : AnAction() { ).execute() val result = response.body() if (response.isSuccessful && result != null) { - val job = result.find { it.jobId == jobInfo.jobId } - if (job != null) waitJobReleasedAndRefresh(jobParentNode, jobInfo) else jobParentNode.cleanCache() + jobsByFilterWaitingPurgeMap[filterNode]?.forEach { data -> + val nodeToFind = data.node as JobNode + val jobAttributes = data.attributes as RemoteJobAttributes + val jobInfo = jobAttributes.jobInfo + val foundJob = result.find { it.jobId == jobInfo.jobId } + if (foundJob != null) foundJobsWaitingInPurgeQueue.add(nodeToFind) + } + if (foundJobsWaitingInPurgeQueue.isNotEmpty()) { + foundJobsWaitingInPurgeQueue.clear() + val filterRefreshSize = jobsByFilterWaitingPurgeMap[filterNode]!!.size + runRefreshByFilter( + filterNode, + if (filterRefreshSize == 1) (filterRefreshSize.toLong() * 1000) else (filterRefreshSize / 2).toLong() * 1000 + ) + } else { + filterNode.cleanCache() + } + } else { + filtersWithRefreshErrors[filterNode] = response } } } + if (filtersWithRefreshErrors.isNotEmpty()) { + var errorFilterNames = "" + for (entry in filtersWithRefreshErrors) { + errorFilterNames += entry.key.unit.name + "\n" + } + throw RuntimeException("Refresh error. Failed filters are: $errorFilterNames") + } + } + + /** + * Function triggers refresh by filter + * @param filter - jes filter + * @param timeWait - time to wait before refresh + */ + private fun runRefreshByFilter(filter: JesFilterNode, timeWait: Long) { + runInEdt { + Thread.sleep(timeWait) + filter.cleanCache() + } + } + + /** + * Function needs to determine when to show "purge job/jobs" action. Currently, purge is only possible within 1 Working Set + * @param jobs - the list of selected job nodes in JES view + * @return true if jobs were selected within 1 WS, false otherwise + */ + private fun isSelectedJobNodesFromSameWS(jobs: List>): Boolean { + val firstJob = jobs[0].node as JobNode + val firstConnection = firstJob.query?.connectionConfig + + // Could be different connections + val diffConnectionJobNode = jobs.filter { + (it.node is JobNode && it.node.unit.connectionConfig != firstConnection) + } + if (diffConnectionJobNode.isNotEmpty()) return false + + // Could be the same connections but different Working Sets + val firstJobWS = if (firstJob.parent is JesFilterNode) firstJob.parent.parent as? JesWsNode else null + if (firstJobWS != null) { + val diffWS = jobs.filter { + val jobWS = it.node.parent?.parent as JesWsNode + firstJobWS.unit.name != jobWS.unit.name + } + if (diffWS.isNotEmpty()) return false + } + return true + } + + /** + * Function builds the filter-to-jobs dependency and puts it to the concurrent map + * @param nodes - list of the selected job nodes in JES view + * @return concurrent map of the filter-to-jobs dependency + */ + private fun selectJobNodesByWSFilters(nodes: List>): ConcurrentHashMap>> { + val jobFilters: MutableList = mutableListOf() + val filtersToJobNodesMap: ConcurrentHashMap>> = ConcurrentHashMap() + nodes.forEach { + val jobNode = it.node as JobNode + val filterNode = findFilter(jobNode) + if (filterNode != null && !jobFilters.contains(filterNode)) { + jobFilters.add(filterNode) + filtersToJobNodesMap[filterNode] = findJobsByFilter(filterNode, nodes) + } + } + return filtersToJobNodesMap + } + + /** + * Function finds the corresponding filter or null if filter was not found + * @param node - job node to find its filter + * @return the JesFilterNode object corresponding to this job node or null if it was not found + */ + private fun findFilter(node: JobNode): JesFilterNode? { + return node.parent as? JesFilterNode + } + + /** + * Function finds all selected job nodes which correspond to the specified filter + * @param filter - filter to find the corresponding job nodes + * @param nodes - the list of all selected job nodes in JES view + * @return the list of job nodes which correspond to the specified filter + */ + private fun findJobsByFilter( + filter: JesFilterNode, + nodes: List> + ): List> { + val jobsByFilter: MutableList> = mutableListOf() + nodes.forEach { + val jobNode = it.node as JobNode + val jobFilter = findFilter(jobNode) + if (filter == jobFilter) { + jobsByFilter.add(it) + } + } + return jobsByFilter + } + + /** + * The main function which triggers the jobs purge on the mainframe and refreshes each affected filter in the JES view tree + * @param jobsByFilterMap - the concurrent map of the filter-to-jobs dependency + * @param nodes - the list of all selected job nodes to purge + * @param e - purge action event + * @param view - an instance of current JES view + */ + private fun runMassivePurgeAndRefreshByFilter( + jobsByFilterMap: ConcurrentHashMap>>, + nodes: List>, + e: AnActionEvent, + view: JesExplorerView + ) { + val dataOpsManager = service() + nodes.forEach { nodeData -> + val jobAttributes = nodeData.attributes as RemoteJobAttributes + val jobStatus = jobAttributes.jobInfo + val connectionConfig = jobAttributes.requesters[0].connectionConfig + runModalTask( + title = "Purging ${jobStatus.jobName}: ${jobStatus.jobId}", + project = e.project, + cancellable = true + ) { + runCatching { + dataOpsManager.performOperation( + operation = PurgeJobOperation( + request = BasicPurgeJobParams(jobStatus.jobName, jobStatus.jobId), + connectionConfig = connectionConfig + ), + progressIndicator = it + ) + }.onFailure { throwable -> + (jobsByFilterMap.values.find { it.contains(nodeData) } as MutableList).remove(nodeData) + view.explorer.showNotification( + "Error purging ${jobStatus.jobName}: ${jobStatus.jobId}", + "${throwable.message}", + NotificationType.ERROR, + e.project + ) + }.onSuccess { + view.explorer.showNotification( + "${jobStatus.jobName}: ${jobStatus.jobId} has been purged", + "$it", + NotificationType.INFORMATION, + e.project + ) + } + } + } + waitJobsReleasedAndRefresh(jobsByFilterMap) } /** @@ -156,9 +306,11 @@ class PurgeJobAction : AnAction() { } if (view is JesExplorerView) { val selected = view.mySelectedNodesData - val node = selected.getOrNull(0)?.node - e.presentation.isVisible = selected.size == 1 - && node is JobNode + val wrongNode = selected.find { it.node !is JobNode } + e.presentation.apply { + isEnabledAndVisible = wrongNode == null && isSelectedJobNodesFromSameWS(selected) + text = if (isEnabledAndVisible && selected.size > 1) "Purge Jobs" else "Purge Job" + } } else if (view is JobBuildTreeView) { val jobStatus = view.getJobLogger().logFetcher.getCachedJobStatus()?.status if (jobStatus == null) { diff --git a/src/main/kotlin/org/zowe/explorer/explorer/actions/SubmitJobAction.kt b/src/main/kotlin/org/zowe/explorer/explorer/actions/SubmitJobAction.kt index b4104eead..84a109ce2 100644 --- a/src/main/kotlin/org/zowe/explorer/explorer/actions/SubmitJobAction.kt +++ b/src/main/kotlin/org/zowe/explorer/explorer/actions/SubmitJobAction.kt @@ -47,6 +47,7 @@ class SubmitJobAction : AnAction() { e.presentation.isEnabledAndVisible = false return } + val project = e.project val selected = view.mySelectedNodesData val node = selected.getOrNull(0)?.node ?: return @@ -78,9 +79,9 @@ class SubmitJobAction : AnAction() { } } }.onSuccess { - view.explorer.showNotification("Job ${it.jobname} has been submitted", "$it", project = e.project) + view.explorer.showNotification("Job ${it.jobname} has been submitted", "$it", project = project) }.onFailure { - view.explorer.reportThrowable(it, e.project) + view.explorer.reportThrowable(it, project) } } } diff --git a/src/main/kotlin/org/zowe/explorer/explorer/actions/SubmitJobToolbarAction.kt b/src/main/kotlin/org/zowe/explorer/explorer/actions/SubmitJobToolbarAction.kt index 3686f75ab..15787daa4 100644 --- a/src/main/kotlin/org/zowe/explorer/explorer/actions/SubmitJobToolbarAction.kt +++ b/src/main/kotlin/org/zowe/explorer/explorer/actions/SubmitJobToolbarAction.kt @@ -38,7 +38,8 @@ class SubmitJobToolbarAction : AnAction() { * Opens job log */ override fun actionPerformed(e: AnActionEvent) { - val explorerView = e.project?.let { FileExplorerContentProvider.getInstance().getExplorerView(it) } + val project = e.project + val explorerView = project?.let { FileExplorerContentProvider.getInstance().getExplorerView(it) } val editor = e.getData(CommonDataKeys.EDITOR) ?: return val file = e.getData(CommonDataKeys.VIRTUAL_FILE) ?: return val jclContent = editor.document.text @@ -48,7 +49,7 @@ class SubmitJobToolbarAction : AnAction() { val connectionConfig = parentAttributes.requesters[0].connectionConfig runBackgroundableTask( title = "Submitting job from file ${file.name}", - project = e.project, + project = project, cancellable = true ) { runCatching { @@ -64,7 +65,7 @@ class SubmitJobToolbarAction : AnAction() { } } }.onFailure { - explorerView?.explorer?.reportThrowable(it, e.project) + explorerView?.explorer?.reportThrowable(it, project) } } } diff --git a/src/main/kotlin/org/zowe/explorer/explorer/actions/TsoSessionCreateAction.kt b/src/main/kotlin/org/zowe/explorer/explorer/actions/TsoSessionCreateAction.kt index 9972f7880..053121f26 100644 --- a/src/main/kotlin/org/zowe/explorer/explorer/actions/TsoSessionCreateAction.kt +++ b/src/main/kotlin/org/zowe/explorer/explorer/actions/TsoSessionCreateAction.kt @@ -14,8 +14,10 @@ import com.intellij.openapi.actionSystem.ActionUpdateThread import com.intellij.openapi.actionSystem.AnAction import com.intellij.openapi.actionSystem.AnActionEvent import com.intellij.openapi.components.service +import com.intellij.openapi.progress.ProcessCanceledException import com.intellij.openapi.ui.Messages import com.intellij.util.containers.isEmpty +import org.zowe.explorer.common.message import org.zowe.explorer.common.ui.showUntilDone import org.zowe.explorer.config.configCrudable import org.zowe.explorer.config.connect.ConnectionConfig @@ -65,7 +67,12 @@ class TsoSessionCreateAction : AnAction() { } } if (throwable != null) { - val errorTemplate = "An error occurred. See details below:\n $throwable" + val tMessage = if (throwable is ProcessCanceledException) { + message("explorer.cancel.by.user.error") + } else { + "${throwable.message}" + } + val errorTemplate = "An error occurred. See details below:\n $tMessage" Messages.showErrorDialog( project, errorTemplate, diff --git a/src/main/kotlin/org/zowe/explorer/explorer/actions/sort/SortAction.kt b/src/main/kotlin/org/zowe/explorer/explorer/actions/sort/SortAction.kt new file mode 100644 index 000000000..e97369dbc --- /dev/null +++ b/src/main/kotlin/org/zowe/explorer/explorer/actions/sort/SortAction.kt @@ -0,0 +1,100 @@ +/* + * This program and the accompanying materials are made available under the terms of the + * Eclipse Public License v2.0 which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-v20.html + * + * SPDX-License-Identifier: EPL-2.0 + * + * Copyright IBA Group 2020 + */ + +package org.zowe.explorer.explorer.actions.sort + +import com.intellij.openapi.actionSystem.* +import org.zowe.explorer.dataops.sort.SortQueryKeys +import org.zowe.explorer.explorer.ui.* + +abstract class SortAction> : ToggleAction() { + + companion object { + fun runRefreshAction(e: AnActionEvent) { + val refreshActionInstance: AnAction = ActionManager.getInstance().getAction("org.zowe.explorer.explorer.actions.RefreshNodeAction") + refreshActionInstance.actionPerformed(e) + } + } + + /** + * Function gets the source view where the action is triggered + * @param e + * @return an instance of ExplorerTreeView + */ + abstract fun getSourceView(e: AnActionEvent) : ExplorerTreeView<*, *, *>? + + /** + * Function gets the source node on which the action is triggered + * @param view + * @return an instance of Node + */ + abstract fun getSourceNode(view: ExplorerTreeView<*, *, *>) : Node? + + /** + * Function performs the query update for the selected node and adds/removes the selected sort key + * @param selectedNode + * @param sortKey + */ + abstract fun performQueryUpdateForNode(selectedNode: Node, sortKey: SortQueryKeys) + + /** + * Function checks if the sort key is currently enabled for the particular node + * @param selectedNode + * @param sortKey + * @return true if the sort key is currently enabled, false otherwise + */ + abstract fun shouldEnableSortKeyForNode(selectedNode: Node, sortKey: SortQueryKeys) : Boolean + + override fun update(e: AnActionEvent) { + super.update(e) + e.presentation.isEnabledAndVisible = true + } + + /** + * If action is dumb aware or not + */ + override fun isDumbAware(): Boolean { + return true + } + + /** + * Tells that only UI component is affected + */ + override fun getActionUpdateThread(): ActionUpdateThread { + return ActionUpdateThread.EDT + } + + /** + * Action performed method to register the custom behavior when any Sort Key was clicked in UI + */ + override fun setSelected(e: AnActionEvent, state: Boolean) { + val view = getSourceView(e) ?: return + val selectedNode = getSourceNode(view) ?: return + val sortKey = this.templateText?.uppercase()?.replace(" ", "_")?.let { SortQueryKeys.valueOf(it) } + ?: throw Exception("Sort key for the selected action was not found.") + if (isSelected(e)) return + performQueryUpdateForNode(selectedNode, sortKey) + + // Create an instance of ActionEvent from the received sort action and trigger refresh action + val actionEvent = AnActionEvent.createFromDataContext(e.place, null, e.dataContext) + runRefreshAction(actionEvent) + } + + /** + * Custom isSelected method determines if the Sort Key is currently enabled or not. Updates UI by 'tick' mark + */ + override fun isSelected(e: AnActionEvent): Boolean { + val view = getSourceView(e) ?: return false + val selectedNode = getSourceNode(view) ?: return false + val sortKey = this.templateText?.uppercase()?.replace(" ", "_")?.let { SortQueryKeys.valueOf(it) } ?: return false + return shouldEnableSortKeyForNode(selectedNode, sortKey) + } + +} diff --git a/src/main/kotlin/org/zowe/explorer/explorer/actions/sort/SortActionGroup.kt b/src/main/kotlin/org/zowe/explorer/explorer/actions/sort/SortActionGroup.kt new file mode 100644 index 000000000..d770d7fbb --- /dev/null +++ b/src/main/kotlin/org/zowe/explorer/explorer/actions/sort/SortActionGroup.kt @@ -0,0 +1,59 @@ +/* + * This program and the accompanying materials are made available under the terms of the + * Eclipse Public License v2.0 which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-v20.html + * + * SPDX-License-Identifier: EPL-2.0 + * + * Copyright IBA Group 2020 + */ + +package org.zowe.explorer.explorer.actions.sort + +import com.intellij.openapi.actionSystem.ActionUpdateThread +import com.intellij.openapi.actionSystem.AnActionEvent +import com.intellij.openapi.actionSystem.DefaultActionGroup +import org.zowe.explorer.explorer.ui.* + +/** + * Abstract class represents the custom sort action group in the FileExplorerView/JesExplorerView context menu + */ +abstract class SortActionGroup : DefaultActionGroup() { + + /** + * Function gets the source view where the action is triggered + * @param e + * @return an instance of ExplorerTreeView + */ + abstract fun getSourceView(e: AnActionEvent) : ExplorerTreeView<*, *, *>? + + /** + * Function checks if the selected node is suitable to display the Sort actions in the ContextMenu group + * @param node + * @returns true if node is a candidate, false otherwise + */ + abstract fun checkNode(node: ExplorerTreeNode<*, *>) : Boolean + + /** + * Update method to determine if sorting is possible for particular item in the tree + */ + override fun update(e: AnActionEvent) { + val view = getSourceView(e) + view ?: let { + e.presentation.isEnabledAndVisible = false + return + } + val selectedNodes = view.mySelectedNodesData + val treePathFromModel = view.myTree.selectionPath + e.presentation.apply { + isEnabledAndVisible = selectedNodes.size == 1 && view.myTree.isExpanded(treePathFromModel) && checkNode(selectedNodes[0].node) + } + } + + /** + * Tells that only UI component is affected + */ + override fun getActionUpdateThread(): ActionUpdateThread { + return ActionUpdateThread.EDT + } +} diff --git a/src/main/kotlin/org/zowe/explorer/explorer/actions/sort/datasets/DatasetsSortAction.kt b/src/main/kotlin/org/zowe/explorer/explorer/actions/sort/datasets/DatasetsSortAction.kt new file mode 100644 index 000000000..f181af28b --- /dev/null +++ b/src/main/kotlin/org/zowe/explorer/explorer/actions/sort/datasets/DatasetsSortAction.kt @@ -0,0 +1,40 @@ +/* + * This program and the accompanying materials are made available under the terms of the + * Eclipse Public License v2.0 which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-v20.html + * + * SPDX-License-Identifier: EPL-2.0 + * + * Copyright IBA Group 2020 + */ + +package org.zowe.explorer.explorer.actions.sort.datasets + +import com.intellij.openapi.actionSystem.AnActionEvent +import org.zowe.explorer.dataops.BatchedRemoteQuery +import org.zowe.explorer.dataops.sort.SortQueryKeys +import org.zowe.explorer.explorer.actions.sort.SortAction +import org.zowe.explorer.explorer.ui.* +import org.zowe.explorer.utils.castOrNull +import org.zowe.explorer.utils.clearAndMergeWith +import org.zowe.explorer.utils.clearOldKeysAndAddNew + +class DatasetsSortAction : SortAction() { + override fun getSourceView(e: AnActionEvent): FileExplorerView? { + return e.getExplorerView() + } + + override fun getSourceNode(view: ExplorerTreeView<*, *, *>): DSMaskNode? { + return view.mySelectedNodesData[0].node.castOrNull() + } + + override fun shouldEnableSortKeyForNode(selectedNode: DSMaskNode, sortKey: SortQueryKeys): Boolean { + return selectedNode.currentSortQueryKeysList.contains(sortKey) + } + + override fun performQueryUpdateForNode(selectedNode: DSMaskNode, sortKey: SortQueryKeys) { + val queryToUpdate = selectedNode.query as BatchedRemoteQuery + selectedNode.currentSortQueryKeysList.clearOldKeysAndAddNew(sortKey) + queryToUpdate.sortKeys.clearAndMergeWith(selectedNode.currentSortQueryKeysList) + } +} diff --git a/src/main/kotlin/org/zowe/explorer/explorer/actions/sort/datasets/DatasetsSortActionGroup.kt b/src/main/kotlin/org/zowe/explorer/explorer/actions/sort/datasets/DatasetsSortActionGroup.kt new file mode 100644 index 000000000..1cba7c348 --- /dev/null +++ b/src/main/kotlin/org/zowe/explorer/explorer/actions/sort/datasets/DatasetsSortActionGroup.kt @@ -0,0 +1,29 @@ +/* + * This program and the accompanying materials are made available under the terms of the + * Eclipse Public License v2.0 which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-v20.html + * + * SPDX-License-Identifier: EPL-2.0 + * + * Copyright IBA Group 2020 + */ + +package org.zowe.explorer.explorer.actions.sort.datasets + +import com.intellij.openapi.actionSystem.AnActionEvent +import org.zowe.explorer.explorer.actions.sort.SortActionGroup +import org.zowe.explorer.explorer.ui.* + +/** + * Represents the custom PS/PDS/PDSe/GDG files sort action group in the FileExplorerView context menu + */ +class DatasetsSortActionGroup : SortActionGroup() { + override fun getSourceView(e: AnActionEvent): FileExplorerView? { + return e.getExplorerView() + } + + override fun checkNode(node: ExplorerTreeNode<*, *>): Boolean { + return node is DSMaskNode + } + +} diff --git a/src/main/kotlin/org/zowe/explorer/explorer/actions/sort/jobs/JobsSortAction.kt b/src/main/kotlin/org/zowe/explorer/explorer/actions/sort/jobs/JobsSortAction.kt new file mode 100644 index 000000000..3e383d84d --- /dev/null +++ b/src/main/kotlin/org/zowe/explorer/explorer/actions/sort/jobs/JobsSortAction.kt @@ -0,0 +1,44 @@ +/* + * This program and the accompanying materials are made available under the terms of the + * Eclipse Public License v2.0 which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-v20.html + * + * SPDX-License-Identifier: EPL-2.0 + * + * Copyright IBA Group 2020 + */ + +package org.zowe.explorer.explorer.actions.sort.jobs + +import com.intellij.openapi.actionSystem.AnActionEvent +import org.zowe.explorer.dataops.UnitRemoteQueryImpl +import org.zowe.explorer.dataops.sort.SortQueryKeys +import org.zowe.explorer.explorer.actions.sort.SortAction +import org.zowe.explorer.explorer.ui.ExplorerTreeView +import org.zowe.explorer.explorer.ui.JesExplorerView +import org.zowe.explorer.explorer.ui.JesFilterNode +import org.zowe.explorer.explorer.ui.getExplorerView +import org.zowe.explorer.utils.castOrNull +import org.zowe.explorer.utils.clearAndMergeWith +import org.zowe.explorer.utils.clearOldKeysAndAddNew + +class JobsSortAction : SortAction() { + override fun getSourceView(e: AnActionEvent): JesExplorerView? { + return e.getExplorerView() + } + + override fun getSourceNode(view: ExplorerTreeView<*, *, *>): JesFilterNode? { + return view.mySelectedNodesData[0].node.castOrNull() + } + + override fun shouldEnableSortKeyForNode(selectedNode: JesFilterNode, sortKey: SortQueryKeys): Boolean { + return selectedNode.currentSortQueryKeysList.contains(sortKey) + } + + override fun performQueryUpdateForNode(selectedNode: JesFilterNode, sortKey: SortQueryKeys) { + val queryToUpdate = selectedNode.query as UnitRemoteQueryImpl + selectedNode.currentSortQueryKeysList.clearOldKeysAndAddNew(sortKey) + queryToUpdate.sortKeys.clearAndMergeWith(selectedNode.currentSortQueryKeysList) + } + +} diff --git a/src/main/kotlin/org/zowe/explorer/explorer/actions/sort/jobs/JobsSortActionGroup.kt b/src/main/kotlin/org/zowe/explorer/explorer/actions/sort/jobs/JobsSortActionGroup.kt new file mode 100644 index 000000000..a0c6aaa27 --- /dev/null +++ b/src/main/kotlin/org/zowe/explorer/explorer/actions/sort/jobs/JobsSortActionGroup.kt @@ -0,0 +1,29 @@ +/* + * This program and the accompanying materials are made available under the terms of the + * Eclipse Public License v2.0 which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-v20.html + * + * SPDX-License-Identifier: EPL-2.0 + * + * Copyright IBA Group 2020 + */ + +package org.zowe.explorer.explorer.actions.sort.jobs + +import com.intellij.openapi.actionSystem.AnActionEvent +import org.zowe.explorer.explorer.actions.sort.SortActionGroup +import org.zowe.explorer.explorer.ui.* + +/** + * Represents the custom Jobs sort action group in the JesExplorerView context menu + */ +class JobsSortActionGroup : SortActionGroup() { + override fun getSourceView(e: AnActionEvent): JesExplorerView? { + return e.getExplorerView() + } + + override fun checkNode(node: ExplorerTreeNode<*, *>): Boolean { + return node is JesFilterNode + } + +} diff --git a/src/main/kotlin/org/zowe/explorer/explorer/actions/sort/members/MembersSortAction.kt b/src/main/kotlin/org/zowe/explorer/explorer/actions/sort/members/MembersSortAction.kt new file mode 100644 index 000000000..41f48d39f --- /dev/null +++ b/src/main/kotlin/org/zowe/explorer/explorer/actions/sort/members/MembersSortAction.kt @@ -0,0 +1,43 @@ +/* + * This program and the accompanying materials are made available under the terms of the + * Eclipse Public License v2.0 which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-v20.html + * + * SPDX-License-Identifier: EPL-2.0 + * + * Copyright IBA Group 2020 + */ + +package org.zowe.explorer.explorer.actions.sort.members + +import com.intellij.openapi.actionSystem.AnActionEvent +import org.zowe.explorer.dataops.BatchedRemoteQuery +import org.zowe.explorer.dataops.sort.SortQueryKeys +import org.zowe.explorer.explorer.actions.sort.SortAction +import org.zowe.explorer.explorer.ui.ExplorerTreeView +import org.zowe.explorer.explorer.ui.FileExplorerView +import org.zowe.explorer.explorer.ui.LibraryNode +import org.zowe.explorer.explorer.ui.getExplorerView +import org.zowe.explorer.utils.castOrNull +import org.zowe.explorer.utils.clearAndMergeWith +import org.zowe.explorer.utils.clearOldKeysAndAddNew + +class MembersSortAction: SortAction() { + override fun getSourceView(e: AnActionEvent): FileExplorerView? { + return e.getExplorerView() + } + + override fun getSourceNode(view: ExplorerTreeView<*, *, *>): LibraryNode? { + return view.mySelectedNodesData[0].node.castOrNull() + } + + override fun shouldEnableSortKeyForNode(selectedNode: LibraryNode, sortKey: SortQueryKeys): Boolean { + return selectedNode.currentSortQueryKeysList.contains(sortKey) + } + + override fun performQueryUpdateForNode(selectedNode: LibraryNode, sortKey: SortQueryKeys) { + val queryToUpdate = selectedNode.query as BatchedRemoteQuery + selectedNode.currentSortQueryKeysList.clearOldKeysAndAddNew(sortKey) + queryToUpdate.sortKeys.clearAndMergeWith(selectedNode.currentSortQueryKeysList) + } +} diff --git a/src/main/kotlin/org/zowe/explorer/explorer/actions/sort/members/MembersSortActionGroup.kt b/src/main/kotlin/org/zowe/explorer/explorer/actions/sort/members/MembersSortActionGroup.kt new file mode 100644 index 000000000..506b1d9ac --- /dev/null +++ b/src/main/kotlin/org/zowe/explorer/explorer/actions/sort/members/MembersSortActionGroup.kt @@ -0,0 +1,29 @@ +/* + * This program and the accompanying materials are made available under the terms of the + * Eclipse Public License v2.0 which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-v20.html + * + * SPDX-License-Identifier: EPL-2.0 + * + * Copyright IBA Group 2020 + */ + +package org.zowe.explorer.explorer.actions.sort.members + +import com.intellij.openapi.actionSystem.AnActionEvent +import org.zowe.explorer.explorer.actions.sort.SortActionGroup +import org.zowe.explorer.explorer.ui.* + +/** + * Represents the custom members sort action group in the FileExplorerView context menu + */ +class MembersSortActionGroup : SortActionGroup() { + override fun getSourceView(e: AnActionEvent): FileExplorerView? { + return e.getExplorerView() + } + + override fun checkNode(node: ExplorerTreeNode<*, *>): Boolean { + return node is LibraryNode + } + +} diff --git a/src/main/kotlin/org/zowe/explorer/explorer/actions/sort/uss/UssSortAction.kt b/src/main/kotlin/org/zowe/explorer/explorer/actions/sort/uss/UssSortAction.kt new file mode 100644 index 000000000..ec05185e0 --- /dev/null +++ b/src/main/kotlin/org/zowe/explorer/explorer/actions/sort/uss/UssSortAction.kt @@ -0,0 +1,44 @@ +/* + * This program and the accompanying materials are made available under the terms of the + * Eclipse Public License v2.0 which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-v20.html + * + * SPDX-License-Identifier: EPL-2.0 + * + * Copyright IBA Group 2020 + */ + +package org.zowe.explorer.explorer.actions.sort.uss + +import com.intellij.openapi.actionSystem.AnActionEvent +import org.zowe.explorer.dataops.UnitRemoteQueryImpl +import org.zowe.explorer.dataops.sort.SortQueryKeys +import org.zowe.explorer.explorer.actions.sort.SortAction +import org.zowe.explorer.explorer.ui.ExplorerTreeView +import org.zowe.explorer.explorer.ui.FileExplorerView +import org.zowe.explorer.explorer.ui.UssDirNode +import org.zowe.explorer.explorer.ui.getExplorerView +import org.zowe.explorer.utils.castOrNull +import org.zowe.explorer.utils.clearAndMergeWith +import org.zowe.explorer.utils.clearOldKeysAndAddNew + +class UssSortAction : SortAction() { + override fun getSourceView(e: AnActionEvent): FileExplorerView? { + return e.getExplorerView() + } + + override fun getSourceNode(view: ExplorerTreeView<*, *, *>): UssDirNode? { + return view.mySelectedNodesData[0].node.castOrNull() + } + + override fun shouldEnableSortKeyForNode(selectedNode: UssDirNode, sortKey: SortQueryKeys): Boolean { + return selectedNode.currentSortQueryKeysList.contains(sortKey) + } + + override fun performQueryUpdateForNode(selectedNode: UssDirNode, sortKey: SortQueryKeys) { + val queryToUpdate = selectedNode.query as UnitRemoteQueryImpl + selectedNode.currentSortQueryKeysList.clearOldKeysAndAddNew(sortKey) + queryToUpdate.sortKeys.clearAndMergeWith(selectedNode.currentSortQueryKeysList) + } + +} diff --git a/src/main/kotlin/org/zowe/explorer/explorer/actions/sort/uss/UssSortActionGroup.kt b/src/main/kotlin/org/zowe/explorer/explorer/actions/sort/uss/UssSortActionGroup.kt new file mode 100644 index 000000000..10f38bdb6 --- /dev/null +++ b/src/main/kotlin/org/zowe/explorer/explorer/actions/sort/uss/UssSortActionGroup.kt @@ -0,0 +1,28 @@ +/* + * This program and the accompanying materials are made available under the terms of the + * Eclipse Public License v2.0 which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-v20.html + * + * SPDX-License-Identifier: EPL-2.0 + * + * Copyright IBA Group 2020 + */ + +package org.zowe.explorer.explorer.actions.sort.uss + +import com.intellij.openapi.actionSystem.AnActionEvent +import org.zowe.explorer.explorer.actions.sort.SortActionGroup +import org.zowe.explorer.explorer.ui.* + +/** + * Represents the custom USS files sort action group in the FileExplorerView context menu + */ +class UssSortActionGroup : SortActionGroup() { + override fun getSourceView(e: AnActionEvent): FileExplorerView? { + return e.getExplorerView() + } + + override fun checkNode(node: ExplorerTreeNode<*, *>): Boolean { + return node is UssDirNode + } +} diff --git a/src/main/kotlin/org/zowe/explorer/explorer/ui/AddJobsFilterDialog.kt b/src/main/kotlin/org/zowe/explorer/explorer/ui/AddJobsFilterDialog.kt index d1a97cbad..77e651697 100644 --- a/src/main/kotlin/org/zowe/explorer/explorer/ui/AddJobsFilterDialog.kt +++ b/src/main/kotlin/org/zowe/explorer/explorer/ui/AddJobsFilterDialog.kt @@ -13,9 +13,9 @@ package org.zowe.explorer.explorer.ui import com.intellij.openapi.project.Project import com.intellij.openapi.ui.DialogWrapper import com.intellij.ui.components.JBTextField +import com.intellij.ui.dsl.builder.AlignX import com.intellij.ui.dsl.builder.bindText import com.intellij.ui.dsl.builder.panel -import com.intellij.ui.dsl.gridLayout.HorizontalAlign import org.zowe.explorer.common.ui.StatefulComponent import org.zowe.explorer.config.ws.JobFilterStateWithWS import org.zowe.explorer.utils.validateJobFilter @@ -52,7 +52,7 @@ class AddJobsFilterDialog( .validationOnApply { validateJobFilter(it.text, ownerField.text, jobIdField.text, state.ws.masks, it, false) } - .horizontalAlign(HorizontalAlign.FILL) + .align(AlignX.FILL) } row { label("Owner: ") @@ -63,7 +63,7 @@ class AddJobsFilterDialog( .validationOnApply { validateJobFilter(prefixField.text, it.text, jobIdField.text, state.ws.masks, it, false) } - .horizontalAlign(HorizontalAlign.FILL) + .align(AlignX.FILL) } row { label("Job ID: ") @@ -74,7 +74,7 @@ class AddJobsFilterDialog( .validationOnApply { validateJobFilter(prefixField.text, ownerField.text, it.text, state.ws.masks, it, true) } - .horizontalAlign(HorizontalAlign.FILL) + .align(AlignX.FILL) } } } diff --git a/src/main/kotlin/org/zowe/explorer/explorer/ui/AddMemberDialog.kt b/src/main/kotlin/org/zowe/explorer/explorer/ui/AddMemberDialog.kt index 0100ad110..ec29812ef 100644 --- a/src/main/kotlin/org/zowe/explorer/explorer/ui/AddMemberDialog.kt +++ b/src/main/kotlin/org/zowe/explorer/explorer/ui/AddMemberDialog.kt @@ -12,9 +12,9 @@ package org.zowe.explorer.explorer.ui import com.intellij.openapi.project.Project import com.intellij.openapi.ui.DialogWrapper +import com.intellij.ui.dsl.builder.AlignX import com.intellij.ui.dsl.builder.bindText import com.intellij.ui.dsl.builder.panel -import com.intellij.ui.dsl.gridLayout.HorizontalAlign import org.zowe.explorer.common.ui.StatefulComponent import org.zowe.explorer.dataops.operations.MemberAllocationParams import org.zowe.explorer.utils.validateForBlank @@ -33,7 +33,7 @@ class AddMemberDialog(project: Project?, override var state: MemberAllocationPar .bindText(state::memberName) .validationOnApply { validateForBlank(it) ?: validateMemberName(it) } .apply { focused() } - .horizontalAlign(HorizontalAlign.FILL) + .align(AlignX.FILL) } } } diff --git a/src/main/kotlin/org/zowe/explorer/explorer/ui/AddOrEditMaskDialog.kt b/src/main/kotlin/org/zowe/explorer/explorer/ui/AddOrEditMaskDialog.kt index e473d05e6..d25c3b9b7 100644 --- a/src/main/kotlin/org/zowe/explorer/explorer/ui/AddOrEditMaskDialog.kt +++ b/src/main/kotlin/org/zowe/explorer/explorer/ui/AddOrEditMaskDialog.kt @@ -13,11 +13,7 @@ package org.zowe.explorer.explorer.ui import com.intellij.openapi.project.Project import com.intellij.openapi.ui.ComboBox import com.intellij.openapi.ui.DialogWrapper -import com.intellij.ui.dsl.builder.Cell -import com.intellij.ui.dsl.builder.bindItem -import com.intellij.ui.dsl.builder.bindText -import com.intellij.ui.dsl.builder.panel -import com.intellij.ui.dsl.gridLayout.HorizontalAlign +import com.intellij.ui.dsl.builder.* import org.zowe.explorer.common.ui.StatefulComponent import org.zowe.explorer.config.connect.ConnectionConfig import org.zowe.explorer.config.connect.getUsername @@ -125,7 +121,7 @@ class AddOrEditMaskDialog( .apply { component.minimumSize = Dimension(10, component.height) } - .horizontalAlign(HorizontalAlign.FILL) + .align(AlignX.FILL) } } } diff --git a/src/main/kotlin/org/zowe/explorer/explorer/ui/AllocationDialog.kt b/src/main/kotlin/org/zowe/explorer/explorer/ui/AllocationDialog.kt index 64ce37b44..e00fa3db2 100644 --- a/src/main/kotlin/org/zowe/explorer/explorer/ui/AllocationDialog.kt +++ b/src/main/kotlin/org/zowe/explorer/explorer/ui/AllocationDialog.kt @@ -16,11 +16,7 @@ import com.intellij.openapi.ui.ComboBox import com.intellij.openapi.ui.ValidationInfo import com.intellij.ui.SimpleListCellRenderer import com.intellij.ui.components.JBScrollPane -import com.intellij.ui.dsl.builder.bindItem -import com.intellij.ui.dsl.builder.bindText -import com.intellij.ui.dsl.builder.panel -import com.intellij.ui.dsl.builder.toNullableProperty -import com.intellij.ui.dsl.gridLayout.HorizontalAlign +import com.intellij.ui.dsl.builder.* import com.intellij.ui.layout.selectedValueMatches import org.zowe.explorer.common.message import org.zowe.explorer.common.ui.StatefulDialog @@ -89,7 +85,7 @@ class AllocationDialog(project: Project?, config: ConnectionConfig, override var datasetNameField.text.ifEmpty { datasetNameField.text = "${HLQ}." } } .onApply { state.datasetName = state.datasetName.uppercase() } - .horizontalAlign(HorizontalAlign.FILL) + .align(AlignX.FILL) .focused() } row { @@ -102,7 +98,7 @@ class AllocationDialog(project: Project?, config: ConnectionConfig, override var memberNameField.text.ifEmpty { memberNameField.text = "SAMPLE" } } .onApply { state.memberName = state.memberName.uppercase() } - .horizontalAlign(HorizontalAlign.FILL) + .align(AlignX.FILL) } .visibleIf(presetsBox.selectedValueMatches { it == Presets.PDS_WITH_EMPTY_MEMBER || it == Presets.PDS_WITH_SAMPLE_JCL_MEMBER }) row { @@ -148,7 +144,7 @@ class AllocationDialog(project: Project?, config: ConnectionConfig, override var { state.allocationParameters.primaryAllocation = it.toIntOrNull() ?: 0 } ) .also { primaryAllocationField = it.component } - .horizontalAlign(HorizontalAlign.FILL) + .align(AlignX.FILL) } row { label("Secondary allocation: ") @@ -159,7 +155,7 @@ class AllocationDialog(project: Project?, config: ConnectionConfig, override var { state.allocationParameters.secondaryAllocation = it.toIntOrNull() ?: 0 } ) .also { secondaryAllocationField = it.component } - .horizontalAlign(HorizontalAlign.FILL) + .align(AlignX.FILL) } row { label("Directory: ") @@ -176,7 +172,7 @@ class AllocationDialog(project: Project?, config: ConnectionConfig, override var { state.allocationParameters.directoryBlocks = it.toIntOrNull() ?: 0 } ) .also { directoryBlocksField = it.component } - .horizontalAlign(HorizontalAlign.FILL) + .align(AlignX.FILL) } .visibleIf(datasetOrganizationBox.selectedValueMatches { it != DatasetOrganization.PS }) row { @@ -205,7 +201,7 @@ class AllocationDialog(project: Project?, config: ConnectionConfig, override var { state.allocationParameters.recordLength = it.toIntOrNull() } ) .also { recordLengthField = it.component } - .horizontalAlign(HorizontalAlign.FILL) + .align(AlignX.FILL) } row { label("Block size: ") @@ -216,7 +212,7 @@ class AllocationDialog(project: Project?, config: ConnectionConfig, override var { state.allocationParameters.blockSize = it.toIntOrNull() } ) .also { blockSizeField = it.component } - .horizontalAlign(HorizontalAlign.FILL) + .align(AlignX.FILL) } row { label("Average Block Length: ") @@ -227,7 +223,7 @@ class AllocationDialog(project: Project?, config: ConnectionConfig, override var { state.allocationParameters.averageBlockLength = it.toIntOrNull() } ) .also { averageBlockLengthField = it.component } - .horizontalAlign(HorizontalAlign.FILL) + .align(AlignX.FILL) } collapsibleGroup("Advanced Parameters", false) { row { @@ -239,7 +235,7 @@ class AllocationDialog(project: Project?, config: ConnectionConfig, override var { state.allocationParameters.volumeSerial = it } ) .also { advancedParametersField = it.component } - .horizontalAlign(HorizontalAlign.FILL) + .align(AlignX.FILL) } row { label("Device Type: ") @@ -249,7 +245,7 @@ class AllocationDialog(project: Project?, config: ConnectionConfig, override var { state.allocationParameters.deviceType ?: "" }, { state.allocationParameters.deviceType = it } ) - .horizontalAlign(HorizontalAlign.FILL) + .align(AlignX.FILL) } row { label("Storage class: ") @@ -259,7 +255,7 @@ class AllocationDialog(project: Project?, config: ConnectionConfig, override var { state.allocationParameters.storageClass ?: "" }, { state.allocationParameters.storageClass = it } ) - .horizontalAlign(HorizontalAlign.FILL) + .align(AlignX.FILL) } row { label("Management class: ") @@ -269,7 +265,7 @@ class AllocationDialog(project: Project?, config: ConnectionConfig, override var { state.allocationParameters.managementClass ?: "" }, { state.allocationParameters.managementClass = it } ) - .horizontalAlign(HorizontalAlign.FILL) + .align(AlignX.FILL) } row { label("Data class: ") @@ -279,7 +275,7 @@ class AllocationDialog(project: Project?, config: ConnectionConfig, override var { state.allocationParameters.dataClass ?: "" }, { state.allocationParameters.dataClass = it } ) - .horizontalAlign(HorizontalAlign.FILL) + .align(AlignX.FILL) } } } diff --git a/src/main/kotlin/org/zowe/explorer/explorer/ui/CreateFileDialog.kt b/src/main/kotlin/org/zowe/explorer/explorer/ui/CreateFileDialog.kt index b6b091a9b..ede2e7e94 100644 --- a/src/main/kotlin/org/zowe/explorer/explorer/ui/CreateFileDialog.kt +++ b/src/main/kotlin/org/zowe/explorer/explorer/ui/CreateFileDialog.kt @@ -11,10 +11,10 @@ package org.zowe.explorer.explorer.ui import com.intellij.openapi.project.Project +import com.intellij.ui.dsl.builder.AlignX import com.intellij.ui.dsl.builder.bindItem import com.intellij.ui.dsl.builder.bindText import com.intellij.ui.dsl.builder.panel -import com.intellij.ui.dsl.gridLayout.HorizontalAlign import org.zowe.explorer.common.ui.StatefulDialog import org.zowe.explorer.dataops.operations.UssAllocationParams import org.zowe.explorer.utils.validateForBlank @@ -71,7 +71,7 @@ class CreateFileDialog(project: Project?, override var state: CreateFileDialogSt textField() .bindText(state::fileName) .validationOnApply { validateForBlank(it) ?: validateUssFileName(it) } - .horizontalAlign(HorizontalAlign.FILL) + .align(AlignX.FILL) .focused() } row { diff --git a/src/main/kotlin/org/zowe/explorer/explorer/ui/DSMaskNode.kt b/src/main/kotlin/org/zowe/explorer/explorer/ui/DSMaskNode.kt index ff434d739..af95cf33a 100644 --- a/src/main/kotlin/org/zowe/explorer/explorer/ui/DSMaskNode.kt +++ b/src/main/kotlin/org/zowe/explorer/explorer/ui/DSMaskNode.kt @@ -12,14 +12,20 @@ package org.zowe.explorer.explorer.ui import com.intellij.ide.projectView.PresentationData import com.intellij.ide.util.treeView.AbstractTreeNode +import com.intellij.openapi.components.service import com.intellij.openapi.project.Project import com.intellij.ui.SimpleTextAttributes import com.intellij.util.containers.toMutableSmartList import org.zowe.explorer.config.connect.ConnectionConfig import org.zowe.explorer.config.ws.DSMask import org.zowe.explorer.dataops.BatchedRemoteQuery +import org.zowe.explorer.dataops.DataOpsManager import org.zowe.explorer.dataops.RemoteQuery +import org.zowe.explorer.dataops.attributes.RemoteDatasetAttributes +import org.zowe.explorer.dataops.sort.SortQueryKeys +import org.zowe.explorer.dataops.sort.typedSortKeys import org.zowe.explorer.explorer.FilesWorkingSet +import org.zowe.explorer.utils.clearAndMergeWith import org.zowe.explorer.vfs.MFVirtualFile import icons.ForMainframeIcons @@ -31,15 +37,18 @@ class DSMaskNode( project: Project, parent: ExplorerTreeNode, workingSet: FilesWorkingSet, - treeStructure: ExplorerTreeStructureBase + treeStructure: ExplorerTreeStructureBase, + override val currentSortQueryKeysList: List = mutableListOf(SortQueryKeys.DATASET_MODIFICATION_DATE, SortQueryKeys.ASCENDING), + override val sortedNodes: List> = mutableListOf() ) : RemoteMFFileFetchNode( dsMask, project, parent, workingSet, treeStructure -), RefreshableNode { +), RefreshableNode, SortableNode { override fun update(presentation: PresentationData) { presentation.addText(value.mask, SimpleTextAttributes.REGULAR_ATTRIBUTES) presentation.addText(" ${value.volser}", SimpleTextAttributes.GRAYED_ATTRIBUTES) presentation.setIcon(ForMainframeIcons.DatasetMask) + updateRefreshDateAndTime(presentation) } override val query: RemoteQuery? @@ -53,14 +62,14 @@ class DSMaskNode( /** * Returns map of children nodes (datasets and uss files). */ - override fun Collection.toChildrenNodes(): MutableList> { + override fun Collection.toChildrenNodes(): List> { return map { if (it.isDirectory) { LibraryNode(it, notNullProject, this@DSMaskNode, unit, treeStructure) } else { FileLikeDatasetNode(it, notNullProject, this@DSMaskNode, unit, treeStructure) } - }.toMutableSmartList() + }.let { sortChildrenNodes(it, currentSortQueryKeysList) } } override val requestClass = DSMask::class.java @@ -72,4 +81,63 @@ class DSMaskNode( return "Fetching listings for ${query.request.mask}" } + override fun > sortChildrenNodes(childrenNodes: List, sortKeys: List): List { + val listToReturn = mutableListOf() + val psFiles = childrenNodes.filter { it is FileLikeDatasetNode } + val libraries = childrenNodes.filter { it is LibraryNode } + val foundSortKey = sortKeys.firstOrNull { typedSortKeys.contains(it) } + if (foundSortKey != null && foundSortKey == SortQueryKeys.DATASET_TYPE) { + val sortedPSFiles = performDatasetsSorting(psFiles, this@DSMaskNode, SortQueryKeys.DATASET_NAME) + val sortedLibraries = performDatasetsSorting(libraries, this@DSMaskNode, SortQueryKeys.DATASET_NAME) + listToReturn.addAll(sortedLibraries) + listToReturn.addAll(sortedPSFiles) + also { sortedNodes.clearAndMergeWith(listToReturn) } + } else if (foundSortKey != null) { + listToReturn.clearAndMergeWith(performDatasetsSorting(childrenNodes, this@DSMaskNode, foundSortKey)) + } else { + listToReturn.clearAndMergeWith(childrenNodes) + } + return listToReturn + } + + /** + * Function sorts the children nodes by specified sorting key + * @param nodes + * @param mask + * @param sortKey + * @return sorted nodes by specified key + */ + private fun > performDatasetsSorting(nodes: List, mask: DSMaskNode, sortKey: SortQueryKeys) : List { + val sortedNodesInternal: List = if (mask.currentSortQueryKeysList.contains(SortQueryKeys.ASCENDING)) { + nodes.sortedBy { + selector(sortKey).invoke(it) + } + } else { + nodes.sortedByDescending { + selector(sortKey).invoke(it) + } + } + return sortedNodesInternal.also { sortedNodes.clearAndMergeWith(it) } + } + + /** + * Selector which extracts the dataset attributes by specified sort key + * @param key - sort key + * @return String representation of the extracted dataset info of the virtual file + */ + private fun selector(key: SortQueryKeys) : (AbstractTreeNode<*>) -> String? { + return { + val datasetAttributes = when (it) { + is FileLikeDatasetNode -> service().tryToGetAttributes(it.virtualFile) as RemoteDatasetAttributes + is LibraryNode -> service().tryToGetAttributes(it.virtualFile) as RemoteDatasetAttributes + else -> null + } + when (key) { + SortQueryKeys.DATASET_NAME -> datasetAttributes?.datasetInfo?.name + SortQueryKeys.DATASET_MODIFICATION_DATE -> datasetAttributes?.datasetInfo?.lastReferenceDate + else -> null + } + } + } + } diff --git a/src/main/kotlin/org/zowe/explorer/explorer/ui/DatasetPropertiesDialog.kt b/src/main/kotlin/org/zowe/explorer/explorer/ui/DatasetPropertiesDialog.kt index 635c1d331..668078a8a 100644 --- a/src/main/kotlin/org/zowe/explorer/explorer/ui/DatasetPropertiesDialog.kt +++ b/src/main/kotlin/org/zowe/explorer/explorer/ui/DatasetPropertiesDialog.kt @@ -13,9 +13,9 @@ package org.zowe.explorer.explorer.ui import com.intellij.openapi.project.Project import com.intellij.openapi.ui.DialogWrapper import com.intellij.ui.components.JBTabbedPane +import com.intellij.ui.dsl.builder.AlignX import com.intellij.ui.dsl.builder.panel import com.intellij.ui.dsl.builder.text -import com.intellij.ui.dsl.gridLayout.HorizontalAlign import org.zowe.explorer.common.ui.DialogMode import org.zowe.explorer.common.ui.DialogState import org.zowe.explorer.common.ui.StatefulComponent @@ -45,7 +45,7 @@ class DatasetPropertiesDialog(val project: Project?, override var state: Dataset textField() .text(dataset.name) .applyToComponent { isEditable = false } - .horizontalAlign(HorizontalAlign.FILL) + .align(AlignX.FILL) } row { label("Dataset name type: ") @@ -53,7 +53,7 @@ class DatasetPropertiesDialog(val project: Project?, override var state: Dataset textField() .text(dataset.dsnameType?.toString() ?: "") .applyToComponent { isEditable = false } - .horizontalAlign(HorizontalAlign.FILL) + .align(AlignX.FILL) } row { label("Catalog name: ") @@ -61,7 +61,7 @@ class DatasetPropertiesDialog(val project: Project?, override var state: Dataset textField() .text(dataset.catalogName ?: "") .applyToComponent { isEditable = false } - .horizontalAlign(HorizontalAlign.FILL) + .align(AlignX.FILL) } row { label("Volume serials: ") @@ -69,7 +69,7 @@ class DatasetPropertiesDialog(val project: Project?, override var state: Dataset textField() .text(dataset.volumeSerials ?: "") .applyToComponent { isEditable = false } - .horizontalAlign(HorizontalAlign.FILL) + .align(AlignX.FILL) } row { label("Device type: ") @@ -77,7 +77,7 @@ class DatasetPropertiesDialog(val project: Project?, override var state: Dataset textField() .text(dataset.deviceType ?: "") .applyToComponent { isEditable = false } - .horizontalAlign(HorizontalAlign.FILL) + .align(AlignX.FILL) } if (dataset.migrated?.equals(HasMigrated.YES) == true) { row { @@ -87,7 +87,6 @@ class DatasetPropertiesDialog(val project: Project?, override var state: Dataset } ) - tabbedPanel.add( "Data", panel { @@ -104,7 +103,7 @@ class DatasetPropertiesDialog(val project: Project?, override var state: Dataset } ) .applyToComponent { isEditable = false } - .horizontalAlign(HorizontalAlign.FILL) + .align(AlignX.FILL) } row { label("Record format: ") @@ -112,7 +111,7 @@ class DatasetPropertiesDialog(val project: Project?, override var state: Dataset textField() .text(dataset.recordFormat?.toString() ?: "") .applyToComponent { isEditable = false } - .horizontalAlign(HorizontalAlign.FILL) + .align(AlignX.FILL) } row { label("Record length: ") @@ -120,7 +119,7 @@ class DatasetPropertiesDialog(val project: Project?, override var state: Dataset textField() .text(dataset.recordLength?.toString() ?: "") .applyToComponent { isEditable = false } - .horizontalAlign(HorizontalAlign.FILL) + .align(AlignX.FILL) } row { label("Block size: ") @@ -128,7 +127,7 @@ class DatasetPropertiesDialog(val project: Project?, override var state: Dataset textField() .text(dataset.blockSize?.toString() ?: "") .applyToComponent { isEditable = false } - .horizontalAlign(HorizontalAlign.FILL) + .align(AlignX.FILL) } row { label("Size in tracks: ") @@ -136,7 +135,7 @@ class DatasetPropertiesDialog(val project: Project?, override var state: Dataset textField() .text(dataset.sizeInTracks?.toString() ?: "") .applyToComponent { isEditable = false } - .horizontalAlign(HorizontalAlign.FILL) + .align(AlignX.FILL) } row { label("Space units: ") @@ -144,7 +143,7 @@ class DatasetPropertiesDialog(val project: Project?, override var state: Dataset textField() .text(dataset.spaceUnits?.toString() ?: "") .applyToComponent { isEditable = false } - .horizontalAlign(HorizontalAlign.FILL) + .align(AlignX.FILL) } if ("YES" == dataset.spaceOverflowIndicator) { row { @@ -154,8 +153,6 @@ class DatasetPropertiesDialog(val project: Project?, override var state: Dataset } ) - - tabbedPanel.add("Extended", panel { row { label("Current Utilization") @@ -167,7 +164,7 @@ class DatasetPropertiesDialog(val project: Project?, override var state: Dataset textField() .text(dataset.usedTracksOrBlocks?.toString() ?: "") .applyToComponent { isEditable = false } - .horizontalAlign(HorizontalAlign.FILL) + .align(AlignX.FILL) } row { label("Used extents: ") @@ -175,7 +172,7 @@ class DatasetPropertiesDialog(val project: Project?, override var state: Dataset textField() .text(dataset.extendsUsed?.toString() ?: "") .applyToComponent { isEditable = false } - .horizontalAlign(HorizontalAlign.FILL) + .align(AlignX.FILL) } row { label("Dates") @@ -187,7 +184,7 @@ class DatasetPropertiesDialog(val project: Project?, override var state: Dataset textField() .text(dataset.creationDate ?: "") .applyToComponent { isEditable = false } - .horizontalAlign(HorizontalAlign.FILL) + .align(AlignX.FILL) } row { label("Referenced date: ") @@ -195,7 +192,7 @@ class DatasetPropertiesDialog(val project: Project?, override var state: Dataset textField() .text(dataset.lastReferenceDate ?: "") .applyToComponent { isEditable = false } - .horizontalAlign(HorizontalAlign.FILL) + .align(AlignX.FILL) } row { label("Expiration date: ") @@ -203,7 +200,7 @@ class DatasetPropertiesDialog(val project: Project?, override var state: Dataset textField() .text(dataset.expirationDate ?: "") .applyToComponent { isEditable = false } - .horizontalAlign(HorizontalAlign.FILL) + .align(AlignX.FILL) } }) diff --git a/src/main/kotlin/org/zowe/explorer/explorer/ui/EditJobsFilterDialog.kt b/src/main/kotlin/org/zowe/explorer/explorer/ui/EditJobsFilterDialog.kt index ec169dd9e..e9aea5f88 100644 --- a/src/main/kotlin/org/zowe/explorer/explorer/ui/EditJobsFilterDialog.kt +++ b/src/main/kotlin/org/zowe/explorer/explorer/ui/EditJobsFilterDialog.kt @@ -13,9 +13,9 @@ package org.zowe.explorer.explorer.ui import com.intellij.openapi.project.Project import com.intellij.openapi.ui.DialogWrapper import com.intellij.ui.components.JBTextField +import com.intellij.ui.dsl.builder.AlignX import com.intellij.ui.dsl.builder.bindText import com.intellij.ui.dsl.builder.panel -import com.intellij.ui.dsl.gridLayout.HorizontalAlign import org.zowe.explorer.common.ui.StatefulComponent import org.zowe.explorer.config.ws.JobFilterStateWithWS import org.zowe.explorer.config.ws.JobsFilter @@ -57,7 +57,7 @@ class EditJobsFilterDialog( .validationOnApply { validateJobFilter(initJobFilter, it.text, ownerField.text, jobIdField.text, state.ws.masks, it, false) } - .horizontalAlign(HorizontalAlign.FILL) + .align(AlignX.FILL) } row { label("Owner: ") @@ -68,7 +68,7 @@ class EditJobsFilterDialog( .validationOnApply { validateJobFilter(initJobFilter, prefixField.text, it.text, jobIdField.text, state.ws.masks, it, false) } - .horizontalAlign(HorizontalAlign.FILL) + .align(AlignX.FILL) } row { label("Job ID: ") @@ -79,7 +79,7 @@ class EditJobsFilterDialog( .validationOnApply { validateJobFilter(initJobFilter, prefixField.text, ownerField.text, it.text, state.ws.masks, it, true) } - .horizontalAlign(HorizontalAlign.FILL) + .align(AlignX.FILL) } } } diff --git a/src/main/kotlin/org/zowe/explorer/explorer/ui/FileFetchNode.kt b/src/main/kotlin/org/zowe/explorer/explorer/ui/FileFetchNode.kt index c7c868d05..c313e5f3a 100644 --- a/src/main/kotlin/org/zowe/explorer/explorer/ui/FileFetchNode.kt +++ b/src/main/kotlin/org/zowe/explorer/explorer/ui/FileFetchNode.kt @@ -10,21 +10,23 @@ package org.zowe.explorer.explorer.ui +import com.intellij.ide.projectView.PresentationData import com.intellij.ide.util.treeView.AbstractTreeNode import com.intellij.openapi.progress.runBackgroundableTask import com.intellij.openapi.project.Project import com.intellij.openapi.vfs.VirtualFile +import com.intellij.ui.SimpleTextAttributes import com.intellij.ui.tree.LeafState import com.intellij.util.containers.toMutableSmartList import org.zowe.explorer.common.message import org.zowe.explorer.config.connect.ConnectionConfigBase -import org.zowe.explorer.dataops.BatchedRemoteQuery -import org.zowe.explorer.dataops.DataOpsManager -import org.zowe.explorer.dataops.Query +import org.zowe.explorer.dataops.* import org.zowe.explorer.explorer.ExplorerUnit import org.zowe.explorer.utils.castOrNull import org.zowe.explorer.utils.locked import org.zowe.explorer.utils.service +import org.zowe.explorer.utils.toHumanReadableFormat +import java.time.LocalDateTime import java.util.concurrent.atomic.AtomicBoolean import java.util.concurrent.locks.ReentrantLock import kotlin.concurrent.withLock @@ -46,6 +48,8 @@ abstract class FileFetchNode, workingSet: JesWorkingSet, - treeStructure: ExplorerTreeStructureBase + treeStructure: ExplorerTreeStructureBase, + override val currentSortQueryKeysList: List = mutableListOf(SortQueryKeys.JOB_CREATION_DATE, SortQueryKeys.ASCENDING), + override val sortedNodes: List> = mutableListOf() ) : RemoteMFFileFetchNode( jobsFilter, project, parent, workingSet, treeStructure -), RefreshableNode { +), SortableNode, RefreshableNode { override val query: RemoteQuery? get() { @@ -44,18 +52,19 @@ class JesFilterNode( } else null } - override fun Collection.toChildrenNodes(): MutableList> { + override fun Collection.toChildrenNodes(): List> { return map { JobNode(it, notNullProject, this@JesFilterNode, unit, treeStructure) - }.toMutableSmartList() + }.let { sortChildrenNodes(it, currentSortQueryKeysList) } } override val requestClass = JobsFilter::class.java override fun update(presentation: PresentationData) { - presentation.presentableText = value.toString() + presentation.addText(value.toString(), SimpleTextAttributes.REGULAR_ATTRIBUTES) presentation.setIcon(jesFilterIcon) + updateRefreshDateAndTime(presentation) } @@ -63,4 +72,54 @@ class JesFilterNode( return "Fetching jobs for ${query.request}" } + override fun > sortChildrenNodes(childrenNodes: List, sortKeys: List): List { + val listToReturn : List = mutableListOf() + val foundSortKey = sortKeys.firstOrNull { typedSortKeys.contains(it) } + if (foundSortKey != null) { + listToReturn.clearAndMergeWith(performJobsSorting(childrenNodes, this@JesFilterNode, foundSortKey)) + } else { + listToReturn.clearAndMergeWith(childrenNodes) + } + return listToReturn + } + + /** + * Function sorts the children nodes by specified sorting key + * @param nodes + * @param jesFilter + * @param sortKey + * @return sorted nodes by specified key + */ + private fun performJobsSorting(nodes: List>, jesFilter: JesFilterNode, sortKey: SortQueryKeys) : List> { + val sortedNodesInternal: List> = if (jesFilter.currentSortQueryKeysList.contains(SortQueryKeys.ASCENDING)) { + nodes.sortedBy { + selector(sortKey).invoke(it) + } + } else { + nodes.sortedByDescending { + selector(sortKey).invoke(it) + } + } + return sortedNodesInternal.also { sortedNodes.clearAndMergeWith(it) } + } + + /** + * Selector which extracts the job info by specified sort key + * @param key - sort key + * @return String representation of the extracted job info attribute of the virtual file + */ + private fun selector(key: SortQueryKeys) : (AbstractTreeNode<*>) -> String? { + return { + val jobInfo = (service().tryToGetAttributes((it as JobNode).value) as RemoteJobAttributes).jobInfo + when(key) { + SortQueryKeys.JOB_NAME -> jobInfo.jobName + SortQueryKeys.JOB_OWNER -> jobInfo.owner + SortQueryKeys.JOB_STATUS -> jobInfo.status?.value + SortQueryKeys.JOB_ID -> jobInfo.jobId + SortQueryKeys.JOB_CREATION_DATE -> jobInfo.execStarted + SortQueryKeys.JOB_COMPLETION_DATE -> jobInfo.execEnded + else -> null + } + } + } } diff --git a/src/main/kotlin/org/zowe/explorer/explorer/ui/JobNode.kt b/src/main/kotlin/org/zowe/explorer/explorer/ui/JobNode.kt index 4f2384d63..cb8dac4e6 100644 --- a/src/main/kotlin/org/zowe/explorer/explorer/ui/JobNode.kt +++ b/src/main/kotlin/org/zowe/explorer/explorer/ui/JobNode.kt @@ -51,8 +51,6 @@ class JobNode( override val query: RemoteQuery? get() { val connectionConfig = unit.connectionConfig - - return if (connectionConfig != null) { UnitRemoteQueryImpl(JobQuery(value), connectionConfig) } else null diff --git a/src/main/kotlin/org/zowe/explorer/explorer/ui/JobPropertiesDialog.kt b/src/main/kotlin/org/zowe/explorer/explorer/ui/JobPropertiesDialog.kt index 091161b6c..e3a413051 100644 --- a/src/main/kotlin/org/zowe/explorer/explorer/ui/JobPropertiesDialog.kt +++ b/src/main/kotlin/org/zowe/explorer/explorer/ui/JobPropertiesDialog.kt @@ -13,9 +13,9 @@ package org.zowe.explorer.explorer.ui import com.intellij.openapi.project.Project import com.intellij.openapi.ui.DialogWrapper import com.intellij.ui.components.JBTabbedPane +import com.intellij.ui.dsl.builder.AlignX import com.intellij.ui.dsl.builder.panel import com.intellij.ui.dsl.builder.text -import com.intellij.ui.dsl.gridLayout.HorizontalAlign import org.zowe.explorer.common.ui.DialogMode import org.zowe.explorer.common.ui.DialogState import org.zowe.explorer.common.ui.StatefulComponent @@ -53,7 +53,7 @@ class JobPropertiesDialog(val project: Project?, override var state: JobState) : textField() .text(job.jobId) .applyToComponent { isEditable = false } - .horizontalAlign(HorizontalAlign.FILL) + .align(AlignX.FILL) } row { label("Job name: ") @@ -61,7 +61,7 @@ class JobPropertiesDialog(val project: Project?, override var state: JobState) : textField() .text(job.jobName) .applyToComponent { isEditable = false } - .horizontalAlign(HorizontalAlign.FILL) + .align(AlignX.FILL) } row { label("Subsystem: ") @@ -69,7 +69,7 @@ class JobPropertiesDialog(val project: Project?, override var state: JobState) : textField() .text(job.subSystem ?: "") .applyToComponent { isEditable = false } - .horizontalAlign(HorizontalAlign.FILL) + .align(AlignX.FILL) } row { label("Owner: ") @@ -77,7 +77,7 @@ class JobPropertiesDialog(val project: Project?, override var state: JobState) : textField() .text(job.owner) .applyToComponent { isEditable = false } - .horizontalAlign(HorizontalAlign.FILL) + .align(AlignX.FILL) } row { label("Status: ") @@ -85,7 +85,7 @@ class JobPropertiesDialog(val project: Project?, override var state: JobState) : textField() .text(job.status?.toString() ?: "") .applyToComponent { isEditable = false } - .horizontalAlign(HorizontalAlign.FILL) + .align(AlignX.FILL) } row { label("Job type: ") @@ -93,7 +93,7 @@ class JobPropertiesDialog(val project: Project?, override var state: JobState) : textField() .text(job.type.toString()) .applyToComponent { isEditable = false } - .horizontalAlign(HorizontalAlign.FILL) + .align(AlignX.FILL) } row { label("Job class: ") @@ -101,7 +101,7 @@ class JobPropertiesDialog(val project: Project?, override var state: JobState) : textField() .text(job.jobClass ?: "") .applyToComponent { isEditable = false } - .horizontalAlign(HorizontalAlign.FILL) + .align(AlignX.FILL) } row { label("Return code: ") @@ -109,7 +109,7 @@ class JobPropertiesDialog(val project: Project?, override var state: JobState) : textField() .text(job.returnedCode ?: "") .applyToComponent { isEditable = false } - .horizontalAlign(HorizontalAlign.FILL) + .align(AlignX.FILL) } row { label("Job correlator: ") @@ -117,7 +117,7 @@ class JobPropertiesDialog(val project: Project?, override var state: JobState) : textField() .text(job.jobCorrelator ?: "") .applyToComponent { isEditable = false } - .horizontalAlign(HorizontalAlign.FILL) + .align(AlignX.FILL) } }) @@ -130,7 +130,7 @@ class JobPropertiesDialog(val project: Project?, override var state: JobState) : textField() .text(job.phase.toString()) .applyToComponent { isEditable = false } - .horizontalAlign(HorizontalAlign.FILL) + .align(AlignX.FILL) } row { label("Phase name: ") @@ -138,7 +138,7 @@ class JobPropertiesDialog(val project: Project?, override var state: JobState) : textField() .text(job.phaseName) .applyToComponent { isEditable = false } - .horizontalAlign(HorizontalAlign.FILL) + .align(AlignX.FILL) } row { label("URL: ") @@ -146,7 +146,7 @@ class JobPropertiesDialog(val project: Project?, override var state: JobState) : textField() .text(job.url) .applyToComponent { isEditable = false } - .horizontalAlign(HorizontalAlign.FILL) + .align(AlignX.FILL) } row { label("Files URL: ") @@ -154,7 +154,7 @@ class JobPropertiesDialog(val project: Project?, override var state: JobState) : textField() .text(job.filesUrl) .applyToComponent { isEditable = false } - .horizontalAlign(HorizontalAlign.FILL) + .align(AlignX.FILL) } row { label("System executor: ") @@ -162,7 +162,7 @@ class JobPropertiesDialog(val project: Project?, override var state: JobState) : textField() .text(job.execSystem ?: "") .applyToComponent { isEditable = false } - .horizontalAlign(HorizontalAlign.FILL) + .align(AlignX.FILL) } row { label("Reason not running: ") @@ -170,7 +170,7 @@ class JobPropertiesDialog(val project: Project?, override var state: JobState) : textField() .text(job.reasonNotRunning ?: "") .applyToComponent { isEditable = false } - .horizontalAlign(HorizontalAlign.FILL) + .align(AlignX.FILL) } row { label("Run info") @@ -183,7 +183,7 @@ class JobPropertiesDialog(val project: Project?, override var state: JobState) : textField() .text(job.execSubmitted ?: "") .applyToComponent { isEditable = false } - .horizontalAlign(HorizontalAlign.FILL) + .align(AlignX.FILL) } row { label("Job start time: ") @@ -191,7 +191,7 @@ class JobPropertiesDialog(val project: Project?, override var state: JobState) : textField() .text(job.execStarted ?: "") .applyToComponent { isEditable = false } - .horizontalAlign(HorizontalAlign.FILL) + .align(AlignX.FILL) } row { label("Time ended: ") @@ -199,7 +199,7 @@ class JobPropertiesDialog(val project: Project?, override var state: JobState) : textField() .text(job.execEnded ?: "") .applyToComponent { isEditable = false } - .horizontalAlign(HorizontalAlign.FILL) + .align(AlignX.FILL) } }) diff --git a/src/main/kotlin/org/zowe/explorer/explorer/ui/LibraryNode.kt b/src/main/kotlin/org/zowe/explorer/explorer/ui/LibraryNode.kt index e1a966de3..8dc9e0ee5 100644 --- a/src/main/kotlin/org/zowe/explorer/explorer/ui/LibraryNode.kt +++ b/src/main/kotlin/org/zowe/explorer/explorer/ui/LibraryNode.kt @@ -13,6 +13,7 @@ package org.zowe.explorer.explorer.ui import com.intellij.icons.AllIcons import com.intellij.ide.projectView.PresentationData import com.intellij.ide.util.treeView.AbstractTreeNode +import com.intellij.openapi.components.service import com.intellij.openapi.project.Project import com.intellij.ui.SimpleTextAttributes import org.zowe.explorer.config.connect.ConnectionConfig @@ -20,9 +21,13 @@ import org.zowe.explorer.dataops.BatchedRemoteQuery import org.zowe.explorer.dataops.DataOpsManager import org.zowe.explorer.dataops.RemoteQuery import org.zowe.explorer.dataops.attributes.RemoteDatasetAttributes +import org.zowe.explorer.dataops.attributes.RemoteMemberAttributes import org.zowe.explorer.dataops.fetch.LibraryQuery import org.zowe.explorer.dataops.getAttributesService +import org.zowe.explorer.dataops.sort.SortQueryKeys +import org.zowe.explorer.dataops.sort.typedSortKeys import org.zowe.explorer.explorer.FilesWorkingSet +import org.zowe.explorer.utils.clearAndMergeWith import org.zowe.explorer.utils.service import org.zowe.explorer.vfs.MFVirtualFile import icons.ForMainframeIcons @@ -45,10 +50,12 @@ class LibraryNode( project: Project, parent: ExplorerTreeNode, workingSet: FilesWorkingSet, - treeStructure: ExplorerTreeStructureBase + treeStructure: ExplorerTreeStructureBase, + override val currentSortQueryKeysList: List = mutableListOf(SortQueryKeys.MEMBER_MODIFICATION_DATE, SortQueryKeys.ASCENDING), + override val sortedNodes: List> = mutableListOf() ) : RemoteMFFileFetchNode( library, project, parent, workingSet, treeStructure -), RefreshableNode { +), RefreshableNode, SortableNode { override val query: RemoteQuery? get() { @@ -60,7 +67,7 @@ class LibraryNode( } override fun Collection.toChildrenNodes(): List> { - return map { FileLikeDatasetNode(it, notNullProject, this@LibraryNode, unit, treeStructure) } + return sortChildrenNodes(map { FileLikeDatasetNode(it, notNullProject, this@LibraryNode, unit, treeStructure) }, currentSortQueryKeysList) } override val requestClass = LibraryQuery::class.java @@ -68,7 +75,7 @@ class LibraryNode( override fun update(presentation: PresentationData) { presentation.setIcon(if (value.isDirectory) ForMainframeIcons.DatasetMask else AllIcons.FileTypes.Any_type) updateNodeTitleUsingCutBuffer(value.presentableName, presentation) - val dataOpsManager = explorer.componentManager.service() + val dataOpsManager = service() getVolserIfPresent(dataOpsManager, value) ?.let { presentation.addText(it, SimpleTextAttributes.GRAYED_ITALIC_ATTRIBUTES) } } @@ -80,4 +87,52 @@ class LibraryNode( override fun makeFetchTaskTitle(query: RemoteQuery): String { return "Fetching members for ${query.request.library.name}" } + + override fun > sortChildrenNodes(childrenNodes: List, sortKeys: List): List { + val listToReturn : List = mutableListOf() + val foundSortKey = sortKeys.firstOrNull { typedSortKeys.contains(it) } + if (foundSortKey != null) { + listToReturn.clearAndMergeWith(performMembersSorting(childrenNodes, this@LibraryNode, foundSortKey)) + } else { + listToReturn.clearAndMergeWith(childrenNodes) + } + return listToReturn + } + + /** + * Function sorts the children nodes by specified sorting key + * @param nodes + * @param dataset + * @param sortKey + * @return sorted nodes by specified key + */ + private fun performMembersSorting(nodes: List>, dataset: LibraryNode, sortKey: SortQueryKeys) : List> { + val sortedNodesInternal: List> = if (dataset.currentSortQueryKeysList.contains(SortQueryKeys.ASCENDING)) { + nodes.sortedBy { + selector(sortKey).invoke(it) + } + } else { + nodes.sortedByDescending { + selector(sortKey).invoke(it) + } + } + return sortedNodesInternal.also { sortedNodes.clearAndMergeWith(it) } + } + + /** + * Selector which extracts the member info by specified sort key + * @param key - sort key + * @return String representation of the extracted member info attribute of the virtual file + */ + private fun selector(key: SortQueryKeys) : (AbstractTreeNode<*>) -> String? { + return { + val memberInfo = + (service().tryToGetAttributes((it as FileLikeDatasetNode).virtualFile) as RemoteMemberAttributes).info + when (key) { + SortQueryKeys.MEMBER_NAME -> memberInfo.name + SortQueryKeys.MEMBER_MODIFICATION_DATE -> memberInfo.modificationDate + else -> null + } + } + } } diff --git a/src/main/kotlin/org/zowe/explorer/explorer/ui/MemberPropertiesDialog.kt b/src/main/kotlin/org/zowe/explorer/explorer/ui/MemberPropertiesDialog.kt index c0fae2c2d..46c5014e0 100644 --- a/src/main/kotlin/org/zowe/explorer/explorer/ui/MemberPropertiesDialog.kt +++ b/src/main/kotlin/org/zowe/explorer/explorer/ui/MemberPropertiesDialog.kt @@ -13,9 +13,9 @@ package org.zowe.explorer.explorer.ui import com.intellij.openapi.project.Project import com.intellij.openapi.ui.DialogWrapper import com.intellij.ui.components.JBTabbedPane +import com.intellij.ui.dsl.builder.AlignX import com.intellij.ui.dsl.builder.panel import com.intellij.ui.dsl.builder.text -import com.intellij.ui.dsl.gridLayout.HorizontalAlign import org.zowe.explorer.common.ui.DialogMode import org.zowe.explorer.common.ui.DialogState import org.zowe.explorer.common.ui.StatefulComponent @@ -44,7 +44,7 @@ class MemberPropertiesDialog(var project: Project?, override var state: MemberSt textField() .text(member.name) .applyToComponent { isEditable = false } - .horizontalAlign(HorizontalAlign.FILL) + .align(AlignX.FILL) } row { label("Version.Modification: ") @@ -58,7 +58,7 @@ class MemberPropertiesDialog(var project: Project?, override var state: MemberSt } ) .applyToComponent { isEditable = false } - .horizontalAlign(HorizontalAlign.FILL) + .align(AlignX.FILL) } row { label("Create Date: ") @@ -66,7 +66,7 @@ class MemberPropertiesDialog(var project: Project?, override var state: MemberSt textField() .text(member.creationDate ?: "") .applyToComponent { isEditable = false } - .horizontalAlign(HorizontalAlign.FILL) + .align(AlignX.FILL) } row { label("Modification Date: ") @@ -74,7 +74,7 @@ class MemberPropertiesDialog(var project: Project?, override var state: MemberSt textField() .text(member.modificationDate ?: "") .applyToComponent { isEditable = false } - .horizontalAlign(HorizontalAlign.FILL) + .align(AlignX.FILL) } row { label("Modification Time: ") @@ -82,7 +82,7 @@ class MemberPropertiesDialog(var project: Project?, override var state: MemberSt textField() .text(member.lastChangeTime ?: "") .applyToComponent { isEditable = false } - .horizontalAlign(HorizontalAlign.FILL) + .align(AlignX.FILL) } row { label("Userid that Created/Modified: ") @@ -90,7 +90,7 @@ class MemberPropertiesDialog(var project: Project?, override var state: MemberSt textField() .text(member.user ?: "") .applyToComponent { isEditable = false } - .horizontalAlign(HorizontalAlign.FILL) + .align(AlignX.FILL) } } ) @@ -104,7 +104,7 @@ class MemberPropertiesDialog(var project: Project?, override var state: MemberSt textField() .text(member.currentNumberOfRecords?.toString() ?: "") .applyToComponent { isEditable = false } - .horizontalAlign(HorizontalAlign.FILL) + .align(AlignX.FILL) } row { label("Beginning number of records: ") @@ -112,7 +112,7 @@ class MemberPropertiesDialog(var project: Project?, override var state: MemberSt textField() .text(member.beginningNumberOfRecords?.toString() ?: "") .applyToComponent { isEditable = false } - .horizontalAlign(HorizontalAlign.FILL) + .align(AlignX.FILL) } row { label("Number of changed records: ") @@ -120,7 +120,7 @@ class MemberPropertiesDialog(var project: Project?, override var state: MemberSt textField() .text(member.numberOfChangedRecords?.toString() ?: "") .applyToComponent { isEditable = false } - .horizontalAlign(HorizontalAlign.FILL) + .align(AlignX.FILL) } row { val updatePlace = if ("Y" == member.sclm) { @@ -146,7 +146,7 @@ class MemberPropertiesDialog(var project: Project?, override var state: MemberSt textField() .text(member.authorizationCode ?: "") .applyToComponent { isEditable = false } - .horizontalAlign(HorizontalAlign.FILL) + .align(AlignX.FILL) } row { label("Current Member is alias of: ") @@ -154,7 +154,7 @@ class MemberPropertiesDialog(var project: Project?, override var state: MemberSt textField() .text(member.aliasOf ?: "") .applyToComponent { isEditable = false } - .horizontalAlign(HorizontalAlign.FILL) + .align(AlignX.FILL) } row { label("Load module attributes: ") @@ -162,7 +162,7 @@ class MemberPropertiesDialog(var project: Project?, override var state: MemberSt textField() .text(member.loadModuleAttributes ?: "") .applyToComponent { isEditable = false } - .horizontalAlign(HorizontalAlign.FILL) + .align(AlignX.FILL) } row { label("Member AMODE: ") @@ -170,7 +170,7 @@ class MemberPropertiesDialog(var project: Project?, override var state: MemberSt textField() .text(member.amode ?: "") .applyToComponent { isEditable = false } - .horizontalAlign(HorizontalAlign.FILL) + .align(AlignX.FILL) } row { label("Member RMODE: ") @@ -178,7 +178,7 @@ class MemberPropertiesDialog(var project: Project?, override var state: MemberSt textField() .text(member.rmode ?: "") .applyToComponent { isEditable = false } - .horizontalAlign(HorizontalAlign.FILL) + .align(AlignX.FILL) } row { label("Size: ") @@ -186,7 +186,7 @@ class MemberPropertiesDialog(var project: Project?, override var state: MemberSt textField() .text(member.size ?: "") .applyToComponent { isEditable = false } - .horizontalAlign(HorizontalAlign.FILL) + .align(AlignX.FILL) } row { label("Member TTR: ") @@ -194,7 +194,7 @@ class MemberPropertiesDialog(var project: Project?, override var state: MemberSt textField() .text(member.ttr ?: "") .applyToComponent { isEditable = false } - .horizontalAlign(HorizontalAlign.FILL) + .align(AlignX.FILL) } row { label("SSI information for a load module: ") @@ -202,7 +202,7 @@ class MemberPropertiesDialog(var project: Project?, override var state: MemberSt textField() .text(member.ssi ?: "") .applyToComponent { isEditable = false } - .horizontalAlign(HorizontalAlign.FILL) + .align(AlignX.FILL) } } ) diff --git a/src/main/kotlin/org/zowe/explorer/explorer/ui/SortableNode.kt b/src/main/kotlin/org/zowe/explorer/explorer/ui/SortableNode.kt new file mode 100644 index 000000000..9fd1e1116 --- /dev/null +++ b/src/main/kotlin/org/zowe/explorer/explorer/ui/SortableNode.kt @@ -0,0 +1,37 @@ +/* + * This program and the accompanying materials are made available under the terms of the + * Eclipse Public License v2.0 which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-v20.html + * + * SPDX-License-Identifier: EPL-2.0 + * + * Copyright IBA Group 2020 + */ + +package org.zowe.explorer.explorer.ui + +import com.intellij.ide.util.treeView.AbstractTreeNode +import org.zowe.explorer.dataops.sort.SortQueryKeys + +/** + * Interface which represents any sortable Node + */ +interface SortableNode { + /** + * Field which holds and identifies the current sort keys for particular Node + */ + val currentSortQueryKeysList : List + + /** + * Stores the sorted nodes for particular SortableNode + */ + val sortedNodes: List> + + /** + * Method sorts the children nodes regarding the sort keys are currently enabled + * @param sortKeys - Sort keys to check + * @return list of sorted children nodes + */ + fun > sortChildrenNodes(childrenNodes: List, sortKeys: List): List = mutableListOf() + +} diff --git a/src/main/kotlin/org/zowe/explorer/explorer/ui/SpoolFilePropertiesDialog.kt b/src/main/kotlin/org/zowe/explorer/explorer/ui/SpoolFilePropertiesDialog.kt index 737e38f5b..a9fc8176d 100644 --- a/src/main/kotlin/org/zowe/explorer/explorer/ui/SpoolFilePropertiesDialog.kt +++ b/src/main/kotlin/org/zowe/explorer/explorer/ui/SpoolFilePropertiesDialog.kt @@ -13,9 +13,9 @@ package org.zowe.explorer.explorer.ui import com.intellij.openapi.project.Project import com.intellij.openapi.ui.DialogWrapper import com.intellij.ui.components.JBTabbedPane +import com.intellij.ui.dsl.builder.AlignX import com.intellij.ui.dsl.builder.panel import com.intellij.ui.dsl.builder.text -import com.intellij.ui.dsl.gridLayout.HorizontalAlign import org.zowe.explorer.common.ui.DialogMode import org.zowe.explorer.common.ui.DialogState import org.zowe.explorer.common.ui.StatefulComponent @@ -55,7 +55,7 @@ class SpoolFilePropertiesDialog(val project: Project?, override var state: Spool textField() .text(spoolFile.jobId) .applyToComponent { isEditable = false } - .horizontalAlign(HorizontalAlign.FILL) + .align(AlignX.FILL) } row { label("Job name: ") @@ -63,7 +63,7 @@ class SpoolFilePropertiesDialog(val project: Project?, override var state: Spool textField() .text(spoolFile.jobname) .applyToComponent { isEditable = false } - .horizontalAlign(HorizontalAlign.FILL) + .align(AlignX.FILL) } row { label("Job correlator: ") @@ -71,7 +71,7 @@ class SpoolFilePropertiesDialog(val project: Project?, override var state: Spool textField() .text(spoolFile.jobCorrelator ?: "") .applyToComponent { isEditable = false } - .horizontalAlign(HorizontalAlign.FILL) + .align(AlignX.FILL) } row { label("Class: ") @@ -79,7 +79,7 @@ class SpoolFilePropertiesDialog(val project: Project?, override var state: Spool textField() .text(spoolFile.fileClass) .applyToComponent { isEditable = false } - .horizontalAlign(HorizontalAlign.FILL) + .align(AlignX.FILL) } row { label("ID: ") @@ -87,7 +87,7 @@ class SpoolFilePropertiesDialog(val project: Project?, override var state: Spool textField() .text(spoolFile.id.toString()) .applyToComponent { isEditable = false } - .horizontalAlign(HorizontalAlign.FILL) + .align(AlignX.FILL) } row { label("DD name: ") @@ -95,7 +95,7 @@ class SpoolFilePropertiesDialog(val project: Project?, override var state: Spool textField() .text(spoolFile.ddName) .applyToComponent { isEditable = false } - .horizontalAlign(HorizontalAlign.FILL) + .align(AlignX.FILL) } row { label("Step name: ") @@ -103,7 +103,7 @@ class SpoolFilePropertiesDialog(val project: Project?, override var state: Spool textField() .text(spoolFile.stepName ?: "") .applyToComponent { isEditable = false } - .horizontalAlign(HorizontalAlign.FILL) + .align(AlignX.FILL) } row { label("Process step: ") @@ -111,7 +111,7 @@ class SpoolFilePropertiesDialog(val project: Project?, override var state: Spool textField() .text(spoolFile.procStep ?: "") .applyToComponent { isEditable = false } - .horizontalAlign(HorizontalAlign.FILL) + .align(AlignX.FILL) } }) tabbedPanel.add( @@ -123,7 +123,7 @@ class SpoolFilePropertiesDialog(val project: Project?, override var state: Spool textField() .text(spoolFile.recfm) .applyToComponent { isEditable = false } - .horizontalAlign(HorizontalAlign.FILL) + .align(AlignX.FILL) } row { label("Byte content: ") @@ -131,7 +131,7 @@ class SpoolFilePropertiesDialog(val project: Project?, override var state: Spool textField() .text(spoolFile.byteCount.toString()) .applyToComponent { isEditable = false } - .horizontalAlign(HorizontalAlign.FILL) + .align(AlignX.FILL) } row { label("Record count: ") @@ -139,7 +139,7 @@ class SpoolFilePropertiesDialog(val project: Project?, override var state: Spool textField() .text(spoolFile.recordCount.toString()) .applyToComponent { isEditable = false } - .horizontalAlign(HorizontalAlign.FILL) + .align(AlignX.FILL) } row { label("Record URL: ") @@ -147,7 +147,7 @@ class SpoolFilePropertiesDialog(val project: Project?, override var state: Spool textField() .text(spoolFile.recordsUrl) .applyToComponent { isEditable = false } - .horizontalAlign(HorizontalAlign.FILL) + .align(AlignX.FILL) } row { label("Record length: ") @@ -155,7 +155,7 @@ class SpoolFilePropertiesDialog(val project: Project?, override var state: Spool textField() .text(spoolFile.recordLength.toString()) .applyToComponent { isEditable = false } - .horizontalAlign(HorizontalAlign.FILL) + .align(AlignX.FILL) } row { label("Subsystem: ") @@ -163,7 +163,7 @@ class SpoolFilePropertiesDialog(val project: Project?, override var state: Spool textField() .text(spoolFile.subsystem ?: "") .applyToComponent { isEditable = false } - .horizontalAlign(HorizontalAlign.FILL) + .align(AlignX.FILL) } }) return tabbedPanel diff --git a/src/main/kotlin/org/zowe/explorer/explorer/ui/UssDirNode.kt b/src/main/kotlin/org/zowe/explorer/explorer/ui/UssDirNode.kt index 9222d8c89..c0ae10b26 100644 --- a/src/main/kotlin/org/zowe/explorer/explorer/ui/UssDirNode.kt +++ b/src/main/kotlin/org/zowe/explorer/explorer/ui/UssDirNode.kt @@ -14,16 +14,16 @@ import com.intellij.icons.AllIcons import com.intellij.ide.projectView.PresentationData import com.intellij.ide.util.treeView.AbstractTreeNode import com.intellij.openapi.project.Project +import com.intellij.ui.SimpleTextAttributes import com.intellij.util.IconUtil import org.zowe.explorer.config.connect.ConnectionConfig import org.zowe.explorer.config.ws.UssPath -import org.zowe.explorer.dataops.DataOpsManager -import org.zowe.explorer.dataops.RemoteQuery -import org.zowe.explorer.dataops.UnitRemoteQueryImpl +import org.zowe.explorer.dataops.* import org.zowe.explorer.dataops.attributes.RemoteUssAttributes import org.zowe.explorer.dataops.fetch.UssQuery -import org.zowe.explorer.dataops.getAttributesService +import org.zowe.explorer.dataops.sort.SortQueryKeys import org.zowe.explorer.explorer.FilesWorkingSet +import org.zowe.explorer.utils.clearAndMergeWith import org.zowe.explorer.utils.service import org.zowe.explorer.vfs.MFVirtualFile @@ -47,7 +47,9 @@ class UssDirNode( workingSet: FilesWorkingSet, treeStructure: ExplorerTreeStructureBase, private var vFile: MFVirtualFile? = null, - private val isRootNode: Boolean = false + private val isRootNode: Boolean = false, + override val currentSortQueryKeysList: List = mutableListOf(SortQueryKeys.FILE_MODIFICATION_DATE, SortQueryKeys.ASCENDING), + override val sortedNodes: List> = mutableListOf() ) : RemoteMFFileFetchNode( ussPath, project, parent, workingSet, treeStructure ), UssNode, RefreshableNode { @@ -75,10 +77,7 @@ class UssDirNode( /** Transform the collection of mainframe virtual files to the list of USS children nodes */ override fun Collection.toChildrenNodes(): List> { return find { attributesService.getAttributes(it)?.path == value.path } - ?.also { - vFile = it - treeStructure.registerNode(this@UssDirNode) - } + ?.also { vFile = it } ?.children ?.map { if (it.isDirectory) { @@ -93,7 +92,7 @@ class UssDirNode( } else { UssFileNode(it, notNullProject, this@UssDirNode, unit, treeStructure) } - } ?: listOf() + }?.let { sortChildrenNodes(it, currentSortQueryKeysList) } ?: listOf() } override val requestClass = UssQuery::class.java @@ -137,7 +136,10 @@ class UssDirNode( if (vFile != null) { updateNodeTitleUsingCutBuffer(text, presentation) } else { - presentation.presentableText = text + presentation.addText(text, SimpleTextAttributes.REGULAR_ATTRIBUTES) + } + if (isRootNode) { + updateRefreshDateAndTime(presentation) } } @@ -145,4 +147,92 @@ class UssDirNode( return vFile } + override fun > sortChildrenNodes(childrenNodes: List, sortKeys: List): List { + if (sortKeys.contains(SortQueryKeys.FILE_NAME)) { + if (sortKeys.contains(SortQueryKeys.ASCENDING)) { + return childrenNodes.sortedBy { + when (it) { + is UssDirNode -> it.vFile?.filenameInternal + is UssFileNode -> it.virtualFile.filenameInternal + else -> null + } + }.also { + sortedNodes.clearAndMergeWith(it) + } + } else { + return childrenNodes.sortedByDescending { + when (it) { + is UssDirNode -> it.vFile?.filenameInternal + is UssFileNode -> it.virtualFile.filenameInternal + else -> null + } + }.also { + sortedNodes.clearAndMergeWith(it) + } + } + } else if (sortKeys.contains(SortQueryKeys.FILE_TYPE)) { + val listToReturn = mutableListOf() + val dirs = mutableListOf() + val files = mutableListOf() + val sortedDirs: List + val sortedFiles: List + childrenNodes.forEach { + when (it) { + is UssDirNode -> dirs.add(it) + is UssFileNode -> files.add(it) + } + } + return if (sortKeys.contains(SortQueryKeys.ASCENDING)) { + sortedDirs = dirs.sortedBy { (it as UssDirNode).vFile?.filenameInternal } + sortedFiles = files.sortedBy { (it as UssFileNode).virtualFile.filenameInternal } + listToReturn.addAll(sortedDirs) + listToReturn.addAll(sortedFiles) + listToReturn.also { + sortedNodes.clearAndMergeWith(it) + } + } else { + sortedDirs = dirs.sortedByDescending { (it as UssDirNode).vFile?.filenameInternal } + sortedFiles = files.sortedByDescending { (it as UssFileNode).virtualFile.filenameInternal } + listToReturn.addAll(sortedDirs) + listToReturn.addAll(sortedFiles) + listToReturn.also { + sortedNodes.clearAndMergeWith(it) + } + } + } else if (sortKeys.contains(SortQueryKeys.FILE_MODIFICATION_DATE)) { + return if (sortKeys.contains(SortQueryKeys.ASCENDING)) { + childrenNodes.sortedBy { + modificationTimeSelector().invoke(it) + }.also { + sortedNodes.clearAndMergeWith(it) + } + } else { + childrenNodes.sortedByDescending { + modificationTimeSelector().invoke(it) + }.also { + sortedNodes.clearAndMergeWith(it) + } + } + } else { + return childrenNodes + } + } + + /** + * Selector which extracts modification time for the every child node + * @return String representation of the modification time of the virtual file + */ + private fun modificationTimeSelector(): (AbstractTreeNode<*>) -> String? { + return { + when(it) { + is UssDirNode -> { + it.virtualFile?.let { vFile -> attributesService.getAttributes(vFile) }?.modificationTime + } + is UssFileNode -> { + attributesService.getAttributes(it.virtualFile)?.modificationTime + } + else -> null + } + } + } } diff --git a/src/main/kotlin/org/zowe/explorer/explorer/ui/UssFileNode.kt b/src/main/kotlin/org/zowe/explorer/explorer/ui/UssFileNode.kt index 2ba8304aa..66e3eddf8 100644 --- a/src/main/kotlin/org/zowe/explorer/explorer/ui/UssFileNode.kt +++ b/src/main/kotlin/org/zowe/explorer/explorer/ui/UssFileNode.kt @@ -17,6 +17,7 @@ import com.intellij.openapi.util.Iconable import com.intellij.ui.AnimatedIcon import com.intellij.util.IconUtil import org.zowe.explorer.config.connect.ConnectionConfig +import org.zowe.explorer.dataops.sort.SortQueryKeys import org.zowe.explorer.explorer.ExplorerUnit import org.zowe.explorer.vfs.MFVirtualFile @@ -26,7 +27,9 @@ class UssFileNode( project: Project, parent: ExplorerTreeNode, unit: ExplorerUnit, - treeStructure: ExplorerTreeStructureBase + treeStructure: ExplorerTreeStructureBase, + override val currentSortQueryKeysList: List = mutableListOf(), + override val sortedNodes: List> = mutableListOf() ) : ExplorerUnitTreeNodeBase>( file, project, parent, unit, treeStructure ), UssNode { diff --git a/src/main/kotlin/org/zowe/explorer/explorer/ui/UssFilePropertiesDialog.kt b/src/main/kotlin/org/zowe/explorer/explorer/ui/UssFilePropertiesDialog.kt index 56df2f856..c978874f3 100644 --- a/src/main/kotlin/org/zowe/explorer/explorer/ui/UssFilePropertiesDialog.kt +++ b/src/main/kotlin/org/zowe/explorer/explorer/ui/UssFilePropertiesDialog.kt @@ -18,10 +18,8 @@ import com.intellij.ui.components.JBTabbedPane import com.intellij.ui.dsl.builder.bindItem import com.intellij.ui.dsl.builder.panel import com.intellij.ui.dsl.builder.text -import com.intellij.ui.dsl.gridLayout.HorizontalAlign import org.zowe.explorer.common.ui.StatefulComponent import org.zowe.explorer.dataops.attributes.RemoteUssAttributes -import org.zowe.kotlinsdk.FileModeValue import javax.swing.JComponent import com.intellij.ui.dsl.builder.* import org.zowe.explorer.dataops.content.synchronizer.DEFAULT_BINARY_CHARSET @@ -72,7 +70,7 @@ class UssFilePropertiesDialog(project: Project?, override var state: UssFileStat textField() .text(state.ussAttributes.name) .applyToComponent { isEditable = false } - .horizontalAlign(HorizontalAlign.FILL) + .align(AlignX.FILL) } row { label("Location: ") @@ -80,7 +78,7 @@ class UssFilePropertiesDialog(project: Project?, override var state: UssFileStat textField() .text(state.ussAttributes.parentDirPath) .applyToComponent { isEditable = false } - .horizontalAlign(HorizontalAlign.FILL) + .align(AlignX.FILL) } row { label("Path: ") @@ -88,7 +86,7 @@ class UssFilePropertiesDialog(project: Project?, override var state: UssFileStat textField() .text(state.ussAttributes.path) .applyToComponent { isEditable = false } - .horizontalAlign(HorizontalAlign.FILL) + .align(AlignX.FILL) } row { label("$fileTypeName size: ") @@ -96,7 +94,7 @@ class UssFilePropertiesDialog(project: Project?, override var state: UssFileStat textField() .text("${state.ussAttributes.length} bytes") .applyToComponent { isEditable = false } - .horizontalAlign(HorizontalAlign.FILL) + .align(AlignX.FILL) } row { label("Last modified: ") @@ -104,14 +102,14 @@ class UssFilePropertiesDialog(project: Project?, override var state: UssFileStat textField() .text(state.ussAttributes.modificationTime ?: "") .applyToComponent { isEditable = false } - .horizontalAlign(HorizontalAlign.FILL) + .align(AlignX.FILL) } if (!state.ussAttributes.isDirectory && state.fileIsBeingEditingNow) { row { label("File encoding: ").widthGroup(sameWidthGroup) comboBox = comboBox(getSupportedEncodings()) .bindItem(state.ussAttributes::charset.toNullableProperty()) - .horizontalAlign(HorizontalAlign.FILL) + .align(AlignX.FILL) } row { button("Reset Default Encoding") { @@ -127,7 +125,7 @@ class UssFilePropertiesDialog(project: Project?, override var state: UssFileStat textField() .text(state.ussAttributes.symlinkTarget ?: "") .applyToComponent { isEditable = false } - .horizontalAlign(HorizontalAlign.FILL) + .align(AlignX.FILL) } } } @@ -139,7 +137,7 @@ class UssFilePropertiesDialog(project: Project?, override var state: UssFileStat textField() .text(state.ussAttributes.owner ?: "") .applyToComponent { isEditable = false } - .horizontalAlign(HorizontalAlign.FILL) + .align(AlignX.FILL) } row { label("Group: ") @@ -147,7 +145,7 @@ class UssFilePropertiesDialog(project: Project?, override var state: UssFileStat textField() .text(state.ussAttributes.groupId ?: "") .applyToComponent { isEditable = false } - .horizontalAlign(HorizontalAlign.FILL) + .align(AlignX.FILL) } row { label("The numeric group ID (GID): ") @@ -155,7 +153,7 @@ class UssFilePropertiesDialog(project: Project?, override var state: UssFileStat textField() .text(state.ussAttributes.gid?.toString() ?: "") .applyToComponent { isEditable = false } - .horizontalAlign(HorizontalAlign.FILL) + .align(AlignX.FILL) } row { label("Owner permissions: ") @@ -165,7 +163,7 @@ class UssFilePropertiesDialog(project: Project?, override var state: UssFileStat { state.ussAttributes.fileMode?.owner?.toFileModeValue() }, { state.ussAttributes.fileMode?.owner = it?.mode ?: 0 } ) - .horizontalAlign(HorizontalAlign.FILL) + .align(AlignX.FILL) } row { label("Group permissions: ") @@ -175,7 +173,7 @@ class UssFilePropertiesDialog(project: Project?, override var state: UssFileStat { state.ussAttributes.fileMode?.group?.toFileModeValue() }, { state.ussAttributes.fileMode?.group = it?.mode ?: 0 } ) - .horizontalAlign(HorizontalAlign.FILL) + .align(AlignX.FILL) } row { label("Permissions for all users: ") @@ -185,7 +183,7 @@ class UssFilePropertiesDialog(project: Project?, override var state: UssFileStat { state.ussAttributes.fileMode?.all?.toFileModeValue() }, { state.ussAttributes.fileMode?.all = it?.mode ?: 0 } ) - .horizontalAlign(HorizontalAlign.FILL) + .align(AlignX.FILL) } } diff --git a/src/main/kotlin/org/zowe/explorer/explorer/ui/UssNode.kt b/src/main/kotlin/org/zowe/explorer/explorer/ui/UssNode.kt index a09326167..68f66f5c4 100644 --- a/src/main/kotlin/org/zowe/explorer/explorer/ui/UssNode.kt +++ b/src/main/kotlin/org/zowe/explorer/explorer/ui/UssNode.kt @@ -10,5 +10,7 @@ package org.zowe.explorer.explorer.ui -interface UssNode { -} +/** + * interface which represents any USS Node. Extends SortableNode which implements children nodes sorting method + */ +interface UssNode : SortableNode diff --git a/src/main/kotlin/org/zowe/explorer/ui/build/jobs/JobBuildTreeView.kt b/src/main/kotlin/org/zowe/explorer/ui/build/jobs/JobBuildTreeView.kt index fa38cbc46..e303926b3 100644 --- a/src/main/kotlin/org/zowe/explorer/ui/build/jobs/JobBuildTreeView.kt +++ b/src/main/kotlin/org/zowe/explorer/ui/build/jobs/JobBuildTreeView.kt @@ -41,9 +41,6 @@ import javax.swing.tree.DefaultMutableTreeNode val JOBS_LOG_VIEW = DataKey.create("jobsLogView") const val JOBS_LOG_NOTIFICATION_GROUP_ID = "org.zowe.explorer.explorer.ExplorerNotificationGroup" -const val SUCCESSFUL_JOB_COMPLETION_CODE = 0 -const val SUCCESSFUL_JOB_COMPLETION_CODE_WITH_WARNING = 4 - /** * Console with BuildTree for display job execution process and results. * @param jobLogInfo job process information necessary to get log and status. @@ -122,22 +119,8 @@ class JobBuildTreeView( .getCachedJobStatus() ?.returnedCode ?.uppercase() - var codeWithWarning = false - val result = if (rc == null || rc.contains(Regex("ERR|ABEND|CANCEL|FAIL"))) FailureResultImpl() - else if (rc.contains("CC")) { // result code can be in format "CC nnnn" - val completionCode = rc.split(" ")[1].toInt() - when (completionCode) { - SUCCESSFUL_JOB_COMPLETION_CODE -> SuccessResultImpl() - - SUCCESSFUL_JOB_COMPLETION_CODE_WITH_WARNING -> { - codeWithWarning = true - SuccessResultImpl() - } - - else -> FailureResultImpl() - } - } else SuccessResultImpl() - + val result = if (rc == null || rc.contains(Regex("ERR|ABEND|CANCEL"))) FailureResultImpl() + else SuccessResultImpl() jobLogger.logFetcher.getCachedLog() .forEach { treeConsoleView.onEvent(buildId, FinishEventImpl(it.key.id, buildId, Date().time, it.key.ddName, result)) @@ -147,8 +130,6 @@ class JobBuildTreeView( val buildExecutionNode = (buildNode as DefaultMutableTreeNode).userObject as ExecutionNode if (result is FailureResultImpl) { buildExecutionNode.setIconProvider { AllIcons.General.BalloonError } - } else if (codeWithWarning) { - buildExecutionNode.setIconProvider { AllIcons.General.BalloonWarning } } else { buildExecutionNode.setIconProvider { AllIcons.General.InspectionsOK } } diff --git a/src/main/kotlin/org/zowe/explorer/ui/build/tso/TSOWindowFactory.kt b/src/main/kotlin/org/zowe/explorer/ui/build/tso/TSOWindowFactory.kt index a76def65c..5131c35f6 100644 --- a/src/main/kotlin/org/zowe/explorer/ui/build/tso/TSOWindowFactory.kt +++ b/src/main/kotlin/org/zowe/explorer/ui/build/tso/TSOWindowFactory.kt @@ -35,24 +35,35 @@ import org.zowe.kotlinsdk.TsoResponse import java.net.ConnectException /** - * Interface class which represents callable methods for topics + * Interface class which represents create topic handler */ -interface TSOSessionHandler { +interface TSOSessionCreateHandler { /** * Function which is called when TSO session is created * @param project - a root project * @param newSession - an instance of config wrapper for new TSO session created */ - fun create (project: Project, newSession: TSOConfigWrapper) + fun create(project: Project, newSession: TSOConfigWrapper) +} +/** + * Interface class which represents reconnect topic handler + */ +interface TSOSessionReconnectHandler { /** * Function which is called when we need to reconnect to the disconnected TSO session * @param project - a root project * @param console - an instance of TSO console view * @param oldSession - an instance of TSO session which we want to reconnect to */ - fun reconnect (project: Project, console: TSOConsoleView, oldSession: TSOConfigWrapper) + fun reconnect(project: Project, console: TSOConsoleView, oldSession: TSOConfigWrapper) +} + +/** + * Interface class which represents process command topic handler + */ +interface TSOSessionProcessCommandHandler { /** * Function which is called when we want to process an entered command. @@ -65,7 +76,7 @@ interface TSOSessionHandler { * @param messageData - message data we want to execute * @param processHandler - process handler to display the result of response message in tool window */ - fun processCommand ( + fun processCommand( project: Project, console: TSOConsoleView, session: TSOConfigWrapper, @@ -74,6 +85,12 @@ interface TSOSessionHandler { messageData: MessageData, processHandler: ProcessHandler ) +} + +/** + * Interface class which represents close topic handler + */ +interface TSOSessionCloseHandler { /** * Function which is called when we want to close currently running TSO session @@ -85,17 +102,32 @@ interface TSOSessionHandler { fun close (project: Project, session: TSOConfigWrapper) } +/** + * Interface class which represents reopen topic handler + */ +interface TSOSessionReopenHandler { + /** + * Function which is called when reopen session event is triggered + * @param project + * @param console + */ + fun reopen(project: Project, console: TSOConsoleView) +} + @JvmField -val SESSION_ADDED_TOPIC = Topic.create("tsoSessionAdded", TSOSessionHandler::class.java) +val SESSION_ADDED_TOPIC = Topic.create("tsoSessionAdded", TSOSessionCreateHandler::class.java) @JvmField -val SESSION_RECONNECT_TOPIC = Topic.create("tsoSessionReconnect", TSOSessionHandler::class.java) +val SESSION_RECONNECT_TOPIC = Topic.create("tsoSessionReconnect", TSOSessionReconnectHandler::class.java) @JvmField -val SESSION_COMMAND_ENTERED = Topic.create("tsoSessionCommandTyped", TSOSessionHandler::class.java) +val SESSION_COMMAND_ENTERED = Topic.create("tsoSessionCommandTyped", TSOSessionProcessCommandHandler::class.java) @JvmField -val SESSION_CLOSED_TOPIC = Topic.create("tsoSessionClosed", TSOSessionHandler::class.java) +val SESSION_CLOSED_TOPIC = Topic.create("tsoSessionClosed", TSOSessionCloseHandler::class.java) + +@JvmField +val SESSION_REOPEN_TOPIC = Topic.create("tsoSessionReopen", TSOSessionReopenHandler::class.java) /** * Factory class for building an instance of TSO tool window when TSO session is created @@ -110,20 +142,6 @@ class TSOWindowFactory : ToolWindowFactory { private var currentTsoSession: TSOConfigWrapper? = null private val tsoSessionToConfigMap = mutableMapOf() - /** - * Getter for TSO session wrapper class for each TSO session created - */ - fun getTsoSession(): TSOConfigWrapper? { - return currentTsoSession - } - - /** - * Getter for TSO config map which contains all the TSO session currently created and active - */ - fun getTsoSessionConfigMap(): Map { - return tsoSessionToConfigMap - } - /** * Method is used to parse response for every TSO session created * @param tsoResponse - response from TSO session @@ -184,16 +202,7 @@ class TSOWindowFactory : ToolWindowFactory { contentManager.addContent(content) contentManager.setSelectedContent(content) - val component = toolWindow.contentManager.selectedContent?.component as TSOConsoleView - val processHandler = component.getProcessHandler() - processHandler.notifyTextAvailable(parseTSODataResponse(tsoSession.getTSOResponse()), ProcessOutputType.STDOUT) - - while (tsoSession.getTSOResponseMessageQueue().last().tsoPrompt == null) { - val response = getTsoMessageQueue(tsoSession) - processHandler.notifyTextAvailable(parseTSODataResponse(response), ProcessOutputType.STDOUT) - tsoSession.setTSOResponseMessageQueue(response.tsoData) - } - processHandler.notifyTextAvailable("> ", ProcessOutputType.STDOUT) + fetchNewSessionResponseMessages(tsoContent, tsoSession) } } @@ -227,92 +236,46 @@ class TSOWindowFactory : ToolWindowFactory { subscribe( project = project, topic = SESSION_ADDED_TOPIC, - handler = object : TSOSessionHandler { + handler = object : TSOSessionCreateHandler { override fun create(project: Project, newSession: TSOConfigWrapper) { - val servletKey = newSession.getTSOResponse().servletKey - if (servletKey != null) { - tsoSessionToConfigMap[servletKey] = newSession.getTSOSessionParams() - addToolWindowContent(project, toolWindow, newSession) - } + val servletKey = newSession.getTSOResponse().servletKey ?: throw Exception("Cannot create a new session, because new session ID was not recognized.") + addToolWindowContent(project, toolWindow, newSession) + tsoSessionToConfigMap[servletKey] = newSession.getTSOSessionParams() } - - override fun reconnect(project: Project, console: TSOConsoleView, oldSession: TSOConfigWrapper) {} - - override fun processCommand( - project: Project, - console: TSOConsoleView, - session: TSOConfigWrapper, - command: String, - messageType: MessageType, - messageData: MessageData, - processHandler: ProcessHandler - ) {} - - override fun close(project: Project, session: TSOConfigWrapper) {} } ) + subscribe( project = project, topic = SESSION_RECONNECT_TOPIC, - handler = object : TSOSessionHandler { - - override fun create(project: Project, newSession: TSOConfigWrapper) {} + handler = object : TSOSessionReconnectHandler { override fun reconnect(project: Project, console: TSOConsoleView, oldSession: TSOConfigWrapper) { val oldServletKey = oldSession.getTSOResponse().servletKey - val params = oldSession.getTSOSessionParams() if (tsoSessionToConfigMap[oldServletKey] != null) { - tsoSessionToConfigMap.remove(oldServletKey) - val tsoResponse = service().performOperation( - TsoOperation( - params, - TsoOperationMode.START - ) - ) - val servletKey = tsoResponse.servletKey - if (servletKey != null) { - val config = TSOConfigWrapper(params, tsoResponse) - currentTsoSession = config - console.setTsoSession(config) - tsoSessionToConfigMap[servletKey] = config.getTSOSessionParams() - console.getProcessHandler() - .notifyTextAvailable(parseTSODataResponse(config.getTSOResponse()), ProcessOutputType.STDOUT) - while (config.getTSOResponseMessageQueue().last().tsoPrompt == null) { - val response = getTsoMessageQueue(config) - console.getProcessHandler() - .notifyTextAvailable(parseTSODataResponse(response), ProcessOutputType.STDOUT) - config.setTSOResponseMessageQueue(response.tsoData) - } + val newSessionConfig = createNewSessionFromOldConfig(oldSession) + if (newSessionConfig != null) { + val sessionResponse = newSessionConfig.getTSOResponse() + val newServletKey = sessionResponse.servletKey ?: throw Exception("Cannot reconnect to the session, because new session ID was not recognized.") + currentTsoSession = newSessionConfig + console.setTsoSession(newSessionConfig) + fetchNewSessionResponseMessages(console, newSessionConfig) + tsoSessionToConfigMap.remove(oldServletKey) + tsoSessionToConfigMap[newServletKey] = newSessionConfig.getTSOSessionParams() } else { - throw Exception("Cannot reconnect to the session, because new session ID was not recognized.") + throw Exception("Cannot reconnect to the session, because new session config is missing.") } } else { throw Exception("Cannot reconnect to the session, because session ID was not found.") } } - - override fun processCommand( - project: Project, - console: TSOConsoleView, - session: TSOConfigWrapper, - command: String, - messageType: MessageType, - messageData: MessageData, - processHandler: ProcessHandler - ) {} - - override fun close(project: Project, session: TSOConfigWrapper) {} } ) subscribe( project = project, topic = SESSION_COMMAND_ENTERED, - handler = object : TSOSessionHandler { - - override fun create(project: Project, newSession: TSOConfigWrapper) {} - - override fun reconnect(project: Project, console: TSOConsoleView, oldSession: TSOConfigWrapper) {} + handler = object : TSOSessionProcessCommandHandler { override fun processCommand( project: Project, @@ -358,27 +321,12 @@ class TSOWindowFactory : ToolWindowFactory { } } } - - override fun close(project: Project, session: TSOConfigWrapper) {} } ) subscribe( project = project, topic = SESSION_CLOSED_TOPIC, - handler = object : TSOSessionHandler { - override fun create(project: Project, newSession: TSOConfigWrapper) {} - - override fun reconnect(project: Project, console: TSOConsoleView, oldSession: TSOConfigWrapper) {} - - override fun processCommand( - project: Project, - console: TSOConsoleView, - session: TSOConfigWrapper, - command: String, - messageType: MessageType, - messageData: MessageData, - processHandler: ProcessHandler - ) {} + handler = object : TSOSessionCloseHandler { override fun close(project: Project, session: TSOConfigWrapper) { /** @@ -407,5 +355,59 @@ class TSOWindowFactory : ToolWindowFactory { } } ) + + subscribe( + project = project, + topic = SESSION_REOPEN_TOPIC, + handler = object : TSOSessionReopenHandler { + override fun reopen(project: Project, console: TSOConsoleView) { + val oldConfig = console.getTsoSession() + runInEdt { + toolWindow.contentManager.apply { + selectedContent?.let { removeContent(it, true) } + } + } + val newConfig = createNewSessionFromOldConfig(oldConfig) ?: throw Exception("Unable to establish a new TSO session with parameters: ${oldConfig.getTSOSessionParams()}") + sendTopic(SESSION_ADDED_TOPIC).create(project, newConfig) + } + } + ) + } + + /** + * Method is used to create a new session config from the parameters of the old session config + * @param oldConfig + * @return a new instance of TSOConfigWrapper if a new session was successfully created, null otherwise + */ + private fun createNewSessionFromOldConfig(oldConfig: TSOConfigWrapper) : TSOConfigWrapper? { + val params = oldConfig.getTSOSessionParams() + val tsoResponse = service().performOperation( + TsoOperation( + params, + TsoOperationMode.START + ) + ) + val servletKey = tsoResponse.servletKey + return if (servletKey != null) TSOConfigWrapper(params, tsoResponse) else null } + + /** + * Method is used to fetch and display the welcome messages when a new session is created and a tool window content is added + * @param console + * @param newConfig + */ + private fun fetchNewSessionResponseMessages(console: TSOConsoleView, newConfig: TSOConfigWrapper) { + val sessionResponse = newConfig.getTSOResponse() + val processHandler = console.getProcessHandler() + processHandler + .notifyTextAvailable(parseTSODataResponse(sessionResponse), ProcessOutputType.STDOUT) + while (newConfig.getTSOResponseMessageQueue().last().tsoPrompt == null) { + val response = getTsoMessageQueue(newConfig) + processHandler + .notifyTextAvailable(parseTSODataResponse(response), ProcessOutputType.STDOUT) + newConfig.setTSOResponseMessageQueue(response.tsoData) + } + processHandler.notifyTextAvailable("> ", ProcessOutputType.STDOUT) + } + } diff --git a/src/main/kotlin/org/zowe/explorer/ui/build/tso/ui/TSOConsoleView.kt b/src/main/kotlin/org/zowe/explorer/ui/build/tso/ui/TSOConsoleView.kt index 72f7d5e5d..f21743625 100644 --- a/src/main/kotlin/org/zowe/explorer/ui/build/tso/ui/TSOConsoleView.kt +++ b/src/main/kotlin/org/zowe/explorer/ui/build/tso/ui/TSOConsoleView.kt @@ -10,11 +10,11 @@ package org.zowe.explorer.ui.build.tso.ui -//import com.intellij.ui.layout.cellPanel import com.intellij.execution.process.ProcessEvent import com.intellij.execution.process.ProcessHandler import com.intellij.execution.process.ProcessListener import com.intellij.execution.ui.ExecutionConsole +import com.intellij.openapi.progress.runBackgroundableTask import com.intellij.openapi.project.Project import com.intellij.openapi.ui.ComboBox import com.intellij.openapi.util.Disposer @@ -29,7 +29,7 @@ import org.zowe.explorer.common.isDebugModeEnabled import org.zowe.explorer.dataops.operations.MessageData import org.zowe.explorer.dataops.operations.MessageType import org.zowe.explorer.ui.build.TerminalCommandReceiver -import org.zowe.explorer.ui.build.tso.SESSION_COMMAND_ENTERED +import org.zowe.explorer.ui.build.tso.* import org.zowe.explorer.ui.build.tso.config.TSOConfigWrapper import org.zowe.explorer.ui.build.tso.utils.InputRecognizer import org.zowe.explorer.utils.log @@ -51,6 +51,7 @@ class TSOConsoleView( private lateinit var tsoMessageTypeBox: ComboBox private lateinit var tsoDataTypeBox: ComboBox private lateinit var cancelCommandButton: JButton + private lateinit var reopenSessionButton: JButton private val tsoWidthGroup: String = "TSO_WIDTH_GROUP" private val tsoMessageTypes: List = @@ -75,7 +76,6 @@ class TSOConsoleView( */ private val tsoPanel by lazy { panel { -// cellPanel() row { label("TSO message type").widthGroup(tsoWidthGroup) comboBox( @@ -94,6 +94,17 @@ class TSOConsoleView( tsoDataTypeBox = it.component } }.visible(debugMode) + row { + button("Reopen Session") { + runBackgroundableTask("Re-opening TSO session", project) { + sendTopic(SESSION_REOPEN_TOPIC).reopen(project, this@TSOConsoleView) + } + }.also { + reopenSessionButton = it.component + reopenSessionButton.apply { toolTipText = "The server tries to re-open the current session in case of some troubles (for example console hangs)" } + } + .widthGroup(tsoWidthGroup) + } row { button("Cancel Command (PA1)") { log.info("CANCEL COMMAND (PA1)") @@ -108,6 +119,7 @@ class TSOConsoleView( }.also { cancelCommandButton = it.component } + .widthGroup(tsoWidthGroup) } }.also { it.border = JBEmptyBorder(10, 15, 10, 15) diff --git a/src/main/kotlin/org/zowe/explorer/ui/build/tso/ui/TSOSessionDialog.kt b/src/main/kotlin/org/zowe/explorer/ui/build/tso/ui/TSOSessionDialog.kt index cc3e81cd3..f4d911883 100644 --- a/src/main/kotlin/org/zowe/explorer/ui/build/tso/ui/TSOSessionDialog.kt +++ b/src/main/kotlin/org/zowe/explorer/ui/build/tso/ui/TSOSessionDialog.kt @@ -13,12 +13,7 @@ package org.zowe.explorer.ui.build.tso.ui import com.intellij.openapi.project.Project import com.intellij.ui.CollectionComboBoxModel import com.intellij.ui.SimpleListCellRenderer -import com.intellij.ui.dsl.builder.bindItem -import com.intellij.ui.dsl.builder.bindText -import com.intellij.ui.dsl.builder.panel -import com.intellij.ui.dsl.builder.toNullableProperty -import com.intellij.ui.dsl.gridLayout.HorizontalAlign -import com.intellij.ui.dsl.gridLayout.VerticalAlign +import com.intellij.ui.dsl.builder.* import org.zowe.explorer.common.ui.StatefulDialog import org.zowe.explorer.config.configCrudable import org.zowe.explorer.config.connect.ConnectionConfig @@ -68,8 +63,7 @@ class TSOSessionDialog(project: Project?, override var state: TSOSessionParams) .also { connectionBox = it.component resizableRow() - it.verticalAlign(VerticalAlign.FILL) - it.horizontalAlign(HorizontalAlign.FILL) + it.align(AlignX.FILL.plus(AlignY.FILL)) } } row { @@ -82,8 +76,7 @@ class TSOSessionDialog(project: Project?, override var state: TSOSessionParams) }.also { logonProcField = it.component resizableRow() - it.verticalAlign(VerticalAlign.FILL) - it.horizontalAlign(HorizontalAlign.FILL) + it.align(AlignX.FILL.plus(AlignY.FILL)) }.validationOnInput { validateForBlank(it) } } row { @@ -94,8 +87,7 @@ class TSOSessionDialog(project: Project?, override var state: TSOSessionParams) .also { charsetField = it.component resizableRow() - it.verticalAlign(VerticalAlign.FILL) - it.horizontalAlign(HorizontalAlign.FILL) + it.align(AlignX.FILL.plus(AlignY.FILL)) }.validationOnInput { validateForBlank(it) ?: validateForPositiveInteger(it) } } row { @@ -108,8 +100,7 @@ class TSOSessionDialog(project: Project?, override var state: TSOSessionParams) .also { codepageField = it.component resizableRow() - it.verticalAlign(VerticalAlign.FILL) - it.horizontalAlign(HorizontalAlign.FILL) + it.align(AlignX.FILL.plus(AlignY.FILL)) } } row { @@ -120,8 +111,7 @@ class TSOSessionDialog(project: Project?, override var state: TSOSessionParams) .also { rowsField = it.component resizableRow() - it.verticalAlign(VerticalAlign.FILL) - it.horizontalAlign(HorizontalAlign.FILL) + it.align(AlignX.FILL.plus(AlignY.FILL)) }.validationOnInput { validateForBlank(it) ?: validateForPositiveInteger(it) } } row { @@ -132,8 +122,7 @@ class TSOSessionDialog(project: Project?, override var state: TSOSessionParams) .also { colsField = it.component resizableRow() - it.verticalAlign(VerticalAlign.FILL) - it.horizontalAlign(HorizontalAlign.FILL) + it.align(AlignX.FILL.plus(AlignY.FILL)) }.validationOnInput { validateForBlank(it) ?: validateForPositiveInteger(it) } } row { @@ -144,8 +133,7 @@ class TSOSessionDialog(project: Project?, override var state: TSOSessionParams) .also { acctField = it.component resizableRow() - it.verticalAlign(VerticalAlign.FILL) - it.horizontalAlign(HorizontalAlign.FILL) + it.align(AlignX.FILL.plus(AlignY.FILL)) }.validationOnInput { validateForBlank(it) } } row { @@ -156,8 +144,7 @@ class TSOSessionDialog(project: Project?, override var state: TSOSessionParams) .also { userGroupField = it.component resizableRow() - it.verticalAlign(VerticalAlign.FILL) - it.horizontalAlign(HorizontalAlign.FILL) + it.align(AlignX.FILL.plus(AlignY.FILL)) }.validationOnInput { validateForBlank(it) } } row { @@ -168,8 +155,7 @@ class TSOSessionDialog(project: Project?, override var state: TSOSessionParams) .also { regionField = it.component resizableRow() - it.verticalAlign(VerticalAlign.FILL) - it.horizontalAlign(HorizontalAlign.FILL) + it.align(AlignX.FILL.plus(AlignY.FILL)) }.validationOnInput { validateForBlank(it) ?: validateForPositiveInteger(it) } } row { diff --git a/src/main/kotlin/org/zowe/explorer/utils/miscUtils.kt b/src/main/kotlin/org/zowe/explorer/utils/miscUtils.kt index 2d59e8ea0..af6f4a6c5 100755 --- a/src/main/kotlin/org/zowe/explorer/utils/miscUtils.kt +++ b/src/main/kotlin/org/zowe/explorer/utils/miscUtils.kt @@ -15,9 +15,15 @@ import com.intellij.util.containers.minimalElements import com.intellij.util.containers.toArray import org.zowe.explorer.config.ConfigDeclaration import org.zowe.explorer.config.connect.ConnectionConfig +import org.zowe.explorer.dataops.sort.SortQueryKeys +import org.zowe.explorer.dataops.sort.orderingSortKeys +import org.zowe.explorer.dataops.sort.typedSortKeys import org.zowe.explorer.explorer.WorkingSet import org.zowe.explorer.explorer.ui.ExplorerTreeView import org.zowe.explorer.explorer.ui.ExplorerUnitTreeNodeBase +import java.time.LocalDateTime +import java.time.format.DateTimeFormatter +import java.time.temporal.ChronoUnit import java.util.* import java.util.concurrent.locks.Lock import java.util.concurrent.locks.ReadWriteLock @@ -178,6 +184,39 @@ fun List.mergeWith(another: List): MutableList { return this.plus(another).toSet().toMutableList() } +/** + * Function clears the input list and adds another list elements to the end of this list + * @receiver any kind of MutableList + * @param another + */ +fun List.clearAndMergeWith(another: List) { + (this as MutableList).apply { + clear() + addAll(another) + } +} + +/** + * Function clears the input list and adds the new sortKey to this list or does nothing if sortKey is null + * @receiver Any kind of MutableList of the current sortKeys + * @param toAdd + */ +fun List.clearOldKeysAndAddNew(toAdd: SortQueryKeys?) { + if (toAdd != null) { + if (typedSortKeys.contains(toAdd)) { + (this as MutableList).apply { + removeAll(typedSortKeys.toSet()) + add(toAdd) + } + } else { + (this as MutableList).apply { + removeAll(orderingSortKeys.toSet()) + add(toAdd) + } + } + } +} + val UNIT_CLASS = Unit::class.java inline fun T.applyIfNotNull(v: V?, block: T.(V) -> T): T { @@ -283,3 +322,14 @@ fun > getSelectedNodesWorkingSets(view: Expl fun String.removeTrailingSlashes(): String { return this.replace(Regex("/+$"), "/") } + +/** + * Utility function which transforms LocalDateTime timestamp to human-readable format (without nanos) + * + * @receiver LocalDateTime instance + * @return String representation of LocalDateTime in human-readable format + */ +fun LocalDateTime.toHumanReadableFormat(): String { + return "$dayOfMonth ${month.name} ${toLocalTime().truncatedTo(ChronoUnit.SECONDS).format( + DateTimeFormatter.ISO_LOCAL_TIME)}" +} diff --git a/src/main/kotlin/org/zowe/explorer/utils/ussFileTagUtils.kt b/src/main/kotlin/org/zowe/explorer/utils/ussFileTagUtils.kt index 094a9e9dc..0265cb1d7 100644 --- a/src/main/kotlin/org/zowe/explorer/utils/ussFileTagUtils.kt +++ b/src/main/kotlin/org/zowe/explorer/utils/ussFileTagUtils.kt @@ -12,13 +12,17 @@ package org.zowe.explorer.utils import com.ibm.mq.headers.CCSID import com.intellij.notification.Notification +import com.intellij.notification.NotificationAction import com.intellij.notification.NotificationType import com.intellij.notification.Notifications +import com.intellij.openapi.actionSystem.AnActionEvent import com.intellij.openapi.components.service +import com.intellij.openapi.ui.Messages import org.zowe.explorer.config.connect.ConnectionConfig import org.zowe.explorer.dataops.DataOpsManager import org.zowe.explorer.dataops.attributes.RemoteUssAttributes import org.zowe.explorer.dataops.content.synchronizer.DEFAULT_BINARY_CHARSET +import org.zowe.explorer.dataops.exceptions.CallException import org.zowe.explorer.dataops.operations.uss.ChangeFileTagOperation import org.zowe.explorer.dataops.operations.uss.ChangeFileTagOperationParams import org.zowe.kotlinsdk.FileTagList @@ -177,14 +181,32 @@ fun removeUssFileTag(attributes: RemoteUssAttributes) { * @param title error text. */ private fun notifyError(th: Throwable, title: String) { - Notifications.Bus.notify( - Notification( - FILE_TAG_NOTIFICATION_GROUP_ID, - title, - th.message ?: "", - NotificationType.ERROR - ) - ) + + var details: String = if (th is CallException) { + th.errorParams?.get("details")?.castOrNull>()?.joinToString("\n") ?: "Unknown error" + } else { + "Unknown error" + } + if (details.contains(":")) { + details = details.split(":").last() + } + + Notification( + FILE_TAG_NOTIFICATION_GROUP_ID, + title, + details, + NotificationType.ERROR + ).addAction(object : NotificationAction("More") { + override fun actionPerformed(e: AnActionEvent, notification: Notification) { + Messages.showErrorDialog( + e.project, + th.message ?: th.toString(), + title + ) + } + }).let { + Notifications.Bus.notify(it) + } } private val unsupportedEncodings = listOf( diff --git a/src/main/resources/META-INF/plugin.xml b/src/main/resources/META-INF/plugin.xml index 043793755..0b467f8d9 100755 --- a/src/main/resources/META-INF/plugin.xml +++ b/src/main/resources/META-INF/plugin.xml @@ -445,6 +445,102 @@ Thank you for considering IBA Group for your mainframe needs. class="org.zowe.explorer.explorer.actions.CreateUssDirectoryAction" text="Directory" icon="AllIcons.Nodes.Folder"/> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -619,6 +769,10 @@ Thank you for considering IBA Group for your mainframe needs. + + + + @@ -643,6 +797,8 @@ Thank you for considering IBA Group for your mainframe needs. + + diff --git a/src/main/resources/messages/FMBundle.properties b/src/main/resources/messages/FMBundle.properties index f3234c9a3..df2c8bb69 100755 --- a/src/main/resources/messages/FMBundle.properties +++ b/src/main/resources/messages/FMBundle.properties @@ -20,10 +20,11 @@ configurable.ws.tables.actions.connection.selected.fix.credentials=Fix credentia configurable.ws.tables.actions.connection.selected.fix.url=Fix z/OSMF connection for {0} configurable.ws.dialog.title.add="Add Working Set" title.error="Error" +explorer.cancel.by.user.error=Error during operation execution: operation cancelled by user encoding.reload.or.convert.dialog.title={0}: Reload or Convert to {1} encoding.reload.or.convert.dialog.message=The encoding you'''ve chosen ('{1}') may change the contents of '{0}'.
Do you want to
1. Reload the file from remote in the new encoding '{1}' and overwrite contents (may not display correctly) or
2. Convert the text and overwrite file in the new encoding?
encoding.reload.dialog.title={0}: Reload to {1} encoding.reload.dialog.message=The encoding you'''ve chosen ('{1}') may change the contents of '{0}'.
Do you want to Reload the file from remote in the new encoding '{1}' and overwrite contents (may not display correctly).
encoding.convert.button.error.tooltip=Encoding conversion is not available because more than one project is open allocation.dialog.unit.size.hint.description=For IBM 3390 direct access storage device:
1 CYLINDER = 15 TRACKS
1 TRACK = 56664 BYTES -allocation.dialog.unit.size.hint.title=Allocation unit Size \ No newline at end of file +allocation.dialog.unit.size.hint.title=Allocation unit Size diff --git a/src/test/kotlin/eu/ibagroup/formainframe/explorer/actions/sort/datasets/DatasetsSortActionGroupTestSpec.kt b/src/test/kotlin/eu/ibagroup/formainframe/explorer/actions/sort/datasets/DatasetsSortActionGroupTestSpec.kt new file mode 100644 index 000000000..402b5c082 --- /dev/null +++ b/src/test/kotlin/eu/ibagroup/formainframe/explorer/actions/sort/datasets/DatasetsSortActionGroupTestSpec.kt @@ -0,0 +1,68 @@ +package org.zowe.explorer.explorer.actions.sort.datasets + +import com.intellij.openapi.actionSystem.AnActionEvent +import com.intellij.openapi.actionSystem.DataKey +import org.zowe.explorer.explorer.actions.sort.members.MembersSortActionGroup +import org.zowe.explorer.explorer.ui.DSMaskNode +import org.zowe.explorer.explorer.ui.FileExplorerView +import org.zowe.explorer.explorer.ui.FileLikeDatasetNode +import org.zowe.explorer.explorer.ui.LibraryNode +import org.zowe.explorer.testutils.WithApplicationShouldSpec +import io.kotest.assertions.assertSoftly +import io.kotest.matchers.shouldBe +import io.kotest.matchers.shouldNotBe +import io.mockk.* + +class DatasetsSortActionGroupTestSpec : WithApplicationShouldSpec({ + + afterSpec { + clearAllMocks() + unmockkAll() + } + + context("datasets sort action group spec") { + + val actionEventMock = mockk() + val explorerViewMock = mockk() + // group action to spy + val classUnderTest = spyk(DatasetsSortActionGroup()) + + should("shouldReturnExplorerView_whenGetExplorerView_givenActionEvent") { + every { actionEventMock.getData(any() as DataKey) } returns explorerViewMock + val actualExplorer = classUnderTest.getSourceView(actionEventMock) + + assertSoftly { + actualExplorer shouldNotBe null + actualExplorer is FileExplorerView + } + } + + should("shouldReturnTrue_whenCheckNode_givenDSMaskNode") { + val nodeMock = mockk() + val checkNode = classUnderTest.checkNode(nodeMock) + + assertSoftly { + checkNode shouldBe true + } + } + + should("shouldReturnNull_whenGetExplorerView_givenActionEvent") { + every { actionEventMock.getData(any() as DataKey) } returns null + val actualExplorer = classUnderTest.getSourceView(actionEventMock) + + assertSoftly { + actualExplorer shouldBe null + } + } + + should("shouldReturnFalse_whenCheckNode_givenWrongNode") { + val nodeMock = mockk() + val checkNode = classUnderTest.checkNode(nodeMock) + + assertSoftly { + checkNode shouldBe false + } + } + } + +}) diff --git a/src/test/kotlin/eu/ibagroup/formainframe/explorer/actions/sort/datasets/DatasetsSortActionTestSpec.kt b/src/test/kotlin/eu/ibagroup/formainframe/explorer/actions/sort/datasets/DatasetsSortActionTestSpec.kt new file mode 100644 index 000000000..a34f41897 --- /dev/null +++ b/src/test/kotlin/eu/ibagroup/formainframe/explorer/actions/sort/datasets/DatasetsSortActionTestSpec.kt @@ -0,0 +1,126 @@ +package org.zowe.explorer.explorer.actions.sort.datasets + +import com.intellij.openapi.actionSystem.AnActionEvent +import com.intellij.openapi.actionSystem.DataKey +import org.zowe.explorer.config.ws.DSMask +import org.zowe.explorer.dataops.BatchedRemoteQuery +import org.zowe.explorer.dataops.attributes.RemoteDatasetAttributes +import org.zowe.explorer.dataops.attributes.RemoteJobAttributes +import org.zowe.explorer.dataops.sort.SortQueryKeys +import org.zowe.explorer.explorer.ui.* +import org.zowe.explorer.testutils.WithApplicationShouldSpec +import org.zowe.explorer.vfs.MFVirtualFile +import io.kotest.assertions.assertSoftly +import io.kotest.matchers.collections.shouldContainExactly +import io.kotest.matchers.shouldBe +import io.kotest.matchers.shouldNotBe +import io.mockk.* + +class DatasetsSortActionTestSpec : WithApplicationShouldSpec({ + + afterSpec { + clearAllMocks() + unmockkAll() + } + + context("Datasets sort action") { + + val actionEventMock = mockk() + val explorerViewMock = mockk() + // action to spy + val classUnderTest = spyk(DatasetsSortAction()) + + should("returnSourceView_whenGetSourceView_givenActionEvent") { + every { actionEventMock.getData(any() as DataKey) } returns explorerViewMock + + val actualExplorerView = classUnderTest.getSourceView(actionEventMock) + + assertSoftly { + actualExplorerView shouldNotBe null + actualExplorerView is FileExplorerView + } + } + + should("returnNull_whenGetSourceView_givenActionEvent") { + every { actionEventMock.getData(any() as DataKey) } returns null + + val actualExplorerView = classUnderTest.getSourceView(actionEventMock) + + assertSoftly { + actualExplorerView shouldBe null + } + } + + should("returnSourceNode_whenGetSourceNode_givenView") { + val nodeMock = mockk() + val fileMock = mockk() + val attributesMock = mockk() + val myNodesData = mutableListOf(NodeData(nodeMock, fileMock, attributesMock)) + mockkObject(myNodesData) + every { explorerViewMock.mySelectedNodesData } returns myNodesData + + val actualNode = classUnderTest.getSourceNode(explorerViewMock) + + assertSoftly { + actualNode shouldBe nodeMock + } + } + + should("returnNull_whenGetSourceNode_givenView") { + val nodeMock = mockk() + val fileMock = mockk() + val attributesMock = mockk() + val myNodesData = mutableListOf(NodeData(nodeMock, fileMock, attributesMock)) + mockkObject(myNodesData) + every { explorerViewMock.mySelectedNodesData } returns myNodesData + + val actualNode = classUnderTest.getSourceNode(explorerViewMock) + + assertSoftly { + actualNode shouldBe null + } + } + + should("returnTrue_whenShouldEnableSortKeyForNode_givenSelectedNodeAndSortKey") { + val nodeMock = mockk() + val sortKey = SortQueryKeys.DATASET_NAME + every { nodeMock.currentSortQueryKeysList } returns listOf(SortQueryKeys.DATASET_NAME, SortQueryKeys.ASCENDING) + + val shouldEnableSortKey = classUnderTest.shouldEnableSortKeyForNode(nodeMock, sortKey) + + assertSoftly { + shouldEnableSortKey shouldBe true + } + } + + should("returnFalse_whenShouldEnableSortKeyForNode_givenSelectedNodeAndSortKey") { + val nodeMock = mockk() + val sortKey = SortQueryKeys.DATASET_NAME + every { nodeMock.currentSortQueryKeysList } returns listOf() + + val shouldEnableSortKey = classUnderTest.shouldEnableSortKeyForNode(nodeMock, sortKey) + + assertSoftly { + shouldEnableSortKey shouldBe false + } + } + + should("updateQuery_whenPerformQueryUpdateForNode_givenSelectedNodeAndSortKey") { + val batchedQueryMock = mockk>() + val nodeMock = mockk() + val sortKey = SortQueryKeys.DATASET_NAME + val expectedSortKeys = listOf(SortQueryKeys.ASCENDING, SortQueryKeys.DATASET_NAME) + every { nodeMock.query } returns batchedQueryMock + every { batchedQueryMock.sortKeys } returns mutableListOf(SortQueryKeys.ASCENDING, SortQueryKeys.DATASET_MODIFICATION_DATE) + every { nodeMock.currentSortQueryKeysList } returns mutableListOf(SortQueryKeys.ASCENDING, SortQueryKeys.DATASET_MODIFICATION_DATE) + + classUnderTest.performQueryUpdateForNode(nodeMock, sortKey) + + assertSoftly { + batchedQueryMock.sortKeys shouldContainExactly expectedSortKeys + } + } + + } + +}) diff --git a/src/test/kotlin/eu/ibagroup/formainframe/explorer/actions/sort/jobs/JobsSortActionGroupTestSpec.kt b/src/test/kotlin/eu/ibagroup/formainframe/explorer/actions/sort/jobs/JobsSortActionGroupTestSpec.kt new file mode 100644 index 000000000..d7d6f84bb --- /dev/null +++ b/src/test/kotlin/eu/ibagroup/formainframe/explorer/actions/sort/jobs/JobsSortActionGroupTestSpec.kt @@ -0,0 +1,185 @@ +/* + * This program and the accompanying materials are made available under the terms of the + * Eclipse Public License v2.0 which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-v20.html + * + * SPDX-License-Identifier: EPL-2.0 + * + * Copyright IBA Group 2020 + */ + +package org.zowe.explorer.explorer.actions.sort.jobs + +import com.intellij.openapi.actionSystem.ActionUpdateThread +import com.intellij.openapi.actionSystem.AnActionEvent +import com.intellij.openapi.actionSystem.DataKey +import com.intellij.openapi.actionSystem.Presentation +import org.zowe.explorer.config.connect.ConnectionConfig +import org.zowe.explorer.config.ws.JobsFilter +import org.zowe.explorer.dataops.UnitRemoteQueryImpl +import org.zowe.explorer.dataops.attributes.RemoteDatasetAttributes +import org.zowe.explorer.dataops.attributes.RemoteJobAttributes +import org.zowe.explorer.explorer.ui.* +import org.zowe.explorer.testutils.WithApplicationShouldSpec +import org.zowe.explorer.vfs.MFVirtualFile +import io.kotest.assertions.assertSoftly +import io.kotest.matchers.shouldBe +import io.kotest.matchers.shouldNotBe +import io.mockk.* +import javax.swing.tree.TreePath + +class JobsSortActionGroupTestSpec : WithApplicationShouldSpec({ + + afterSpec { + clearAllMocks() + unmockkAll() + } + + context("jobs sort action group spec") { + + // group action to spy + val classUnderTest = spyk(JobsSortActionGroup()) + + val mockedActionEvent = mockk() + val selectionPath = mockk() + val mockedFileExplorerView = mockk() + every { mockedActionEvent.getExplorerView() } returns mockedFileExplorerView + every { mockedFileExplorerView.myTree } returns mockk() + every { mockedFileExplorerView.myTree.selectionPath } returns selectionPath + + // Presentation + val presentation = Presentation() + every { mockedActionEvent.presentation } returns presentation + + // Target UssDirNode + Query for test + val mockedMFVirtualFile = mockk() + val mockedJesFilterNode = mockk() + val mockedJobRemoteAttributes = mockk() + val mockedJobQuery = mockk>() + every { mockedJesFilterNode.virtualFile } returns mockedMFVirtualFile + every { mockedJesFilterNode.query } returns mockedJobQuery + + // NodeData for test + val mockedNodeDataForTest = NodeData(mockedJesFilterNode, mockedMFVirtualFile, mockedJobRemoteAttributes) + mockkObject(mockedNodeDataForTest) + + should("shouldReturnExplorerView_whenGetExplorerView_givenActionEvent") { + every { mockedActionEvent.getData(any() as DataKey) } returns mockedFileExplorerView + val actualExplorer = classUnderTest.getSourceView(mockedActionEvent) + + assertSoftly { + actualExplorer shouldNotBe null + actualExplorer is JesExplorerView + } + } + + should("shouldReturnTrue_whenCheckNode_givenJesFilterNode") { + val nodeMock = mockk() + val checkNode = classUnderTest.checkNode(nodeMock) + + assertSoftly { + checkNode shouldBe true + } + } + + should("shouldReturnNull_whenGetExplorerView_givenActionEvent") { + every { mockedActionEvent.getData(any() as DataKey) } returns null + val actualExplorer = classUnderTest.getSourceView(mockedActionEvent) + + assertSoftly { + actualExplorer shouldBe null + } + } + + should("shouldReturnFalse_whenCheckNode_givenWrongNode") { + val nodeMock = mockk() + val checkNode = classUnderTest.checkNode(nodeMock) + + assertSoftly { + checkNode shouldBe false + } + } + + should("is visible from context menu if file explorer view is null") { + var isVisible = true + every { mockedActionEvent.getExplorerView() } answers { + isVisible = false + null + } + + classUnderTest.update(mockedActionEvent) + assertSoftly { + isVisible shouldBe false + } + } + + should("is visible from context menu if file explorer view is not null and selected node is not UssDirNode") { + var isVisible = true + val mockedNodeDataNotJesFilterForTest = + NodeData(mockk(), mockk(), mockk()) + every { mockedFileExplorerView.mySelectedNodesData } answers { + isVisible = false + listOf(mockedNodeDataNotJesFilterForTest) + } + every { mockedFileExplorerView.myTree.isExpanded(selectionPath) } returns true + every { mockedActionEvent.getExplorerView() } returns mockedFileExplorerView + + classUnderTest.update(mockedActionEvent) + assertSoftly { + isVisible shouldBe false + } + } + + should("is visible from context menu if file explorer view is not null and selected node is UssDirNode and path is expanded") { + var isVisible = false + every { mockedFileExplorerView.mySelectedNodesData } returns listOf(mockedNodeDataForTest) + every { mockedFileExplorerView.myTree.isExpanded(selectionPath) } answers { + isVisible = true + true + } + + classUnderTest.update(mockedActionEvent) + assertSoftly { + isVisible shouldBe true + } + } + + should("is visible from context menu if file explorer view is not null and selected node is UssDirNode and path is not expanded") { + var isVisible = true + every { mockedFileExplorerView.mySelectedNodesData } returns listOf(mockedNodeDataForTest) + every { mockedFileExplorerView.myTree.isExpanded(selectionPath) } answers { + isVisible = false + false + } + + classUnderTest.update(mockedActionEvent) + assertSoftly { + isVisible shouldBe false + } + } + + should("is visible from context menu if file explorer view is not null and selectedNodes size > 1") { + var isVisible = true + every { mockedFileExplorerView.mySelectedNodesData } answers { + isVisible = false + listOf(mockedNodeDataForTest, mockedNodeDataForTest) + } + + classUnderTest.update(mockedActionEvent) + assertSoftly { + isVisible shouldBe false + } + } + + should("return EDT thread_whenGetActionUpdateThread_givenNothing") { + //given + + //when + val thread = classUnderTest.actionUpdateThread + //then + assertSoftly { + thread shouldBe ActionUpdateThread.EDT + } + } + } +}) diff --git a/src/test/kotlin/eu/ibagroup/formainframe/explorer/actions/sort/jobs/JobsSortActionTestSpec.kt b/src/test/kotlin/eu/ibagroup/formainframe/explorer/actions/sort/jobs/JobsSortActionTestSpec.kt new file mode 100644 index 000000000..cc5068b21 --- /dev/null +++ b/src/test/kotlin/eu/ibagroup/formainframe/explorer/actions/sort/jobs/JobsSortActionTestSpec.kt @@ -0,0 +1,135 @@ +/* + * This program and the accompanying materials are made available under the terms of the + * Eclipse Public License v2.0 which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-v20.html + * + * SPDX-License-Identifier: EPL-2.0 + * + * Copyright IBA Group 2020 + */ + +package org.zowe.explorer.explorer.actions.sort.jobs + +import com.intellij.openapi.actionSystem.AnActionEvent +import com.intellij.openapi.actionSystem.DataKey +import org.zowe.explorer.config.connect.ConnectionConfig +import org.zowe.explorer.config.ws.JobsFilter +import org.zowe.explorer.dataops.UnitRemoteQueryImpl +import org.zowe.explorer.dataops.attributes.RemoteDatasetAttributes +import org.zowe.explorer.dataops.attributes.RemoteJobAttributes +import org.zowe.explorer.dataops.sort.SortQueryKeys +import org.zowe.explorer.explorer.ui.* +import org.zowe.explorer.testutils.WithApplicationShouldSpec +import org.zowe.explorer.vfs.MFVirtualFile +import io.kotest.assertions.assertSoftly +import io.kotest.matchers.collections.shouldContainExactly +import io.kotest.matchers.shouldBe +import io.kotest.matchers.shouldNotBe +import io.mockk.* + +class JobsSortActionTestSpec : WithApplicationShouldSpec ({ + + afterSpec { + clearAllMocks() + unmockkAll() + } + + context("Jobs sort action") { + + val actionEventMock = mockk() + val explorerViewMock = mockk() + // action to spy + val classUnderTest = spyk(JobsSortAction()) + + should("returnSourceView_whenGetSourceView_givenActionEvent") { + every { actionEventMock.getData(any() as DataKey) } returns explorerViewMock + + val actualExplorerView = classUnderTest.getSourceView(actionEventMock) + + assertSoftly { + actualExplorerView shouldNotBe null + actualExplorerView is JesExplorerView + } + } + + should("returnNull_whenGetSourceView_givenActionEvent") { + every { actionEventMock.getData(any() as DataKey) } returns null + + val actualExplorerView = classUnderTest.getSourceView(actionEventMock) + + assertSoftly { + actualExplorerView shouldBe null + } + } + + should("returnSourceNode_whenGetSourceNode_givenView") { + val nodeMock = mockk() + val fileMock = mockk() + val attributesMock = mockk() + val myNodesData = mutableListOf(NodeData(nodeMock, fileMock, attributesMock)) + mockkObject(myNodesData) + every { explorerViewMock.mySelectedNodesData } returns myNodesData + + val actualNode = classUnderTest.getSourceNode(explorerViewMock) + + assertSoftly { + actualNode shouldBe nodeMock + } + } + + should("returnNull_whenGetSourceNode_givenView") { + val nodeMock = mockk() + val fileMock = mockk() + val attributesMock = mockk() + val myNodesData = mutableListOf(NodeData(nodeMock, fileMock, attributesMock)) + mockkObject(myNodesData) + every { explorerViewMock.mySelectedNodesData } returns myNodesData + + val actualNode = classUnderTest.getSourceNode(explorerViewMock) + + assertSoftly { + actualNode shouldBe null + } + } + + should("returnTrue_whenShouldEnableSortKeyForNode_givenSelectedNodeAndSortKey") { + val nodeMock = mockk() + val sortKey = SortQueryKeys.JOB_NAME + every { nodeMock.currentSortQueryKeysList } returns listOf(SortQueryKeys.JOB_NAME, SortQueryKeys.ASCENDING) + + val shouldEnableSortKey = classUnderTest.shouldEnableSortKeyForNode(nodeMock, sortKey) + + assertSoftly { + shouldEnableSortKey shouldBe true + } + } + + should("returnFalse_whenShouldEnableSortKeyForNode_givenSelectedNodeAndSortKey") { + val nodeMock = mockk() + val sortKey = SortQueryKeys.JOB_NAME + every { nodeMock.currentSortQueryKeysList } returns listOf() + + val shouldEnableSortKey = classUnderTest.shouldEnableSortKeyForNode(nodeMock, sortKey) + + assertSoftly { + shouldEnableSortKey shouldBe false + } + } + + should("updateQuery_whenPerformQueryUpdateForNode_givenSelectedNodeAndSortKey") { + val jobQueryMock = mockk>() + val nodeMock = mockk() + val sortKey = SortQueryKeys.JOB_NAME + val expectedSortKeys = listOf(SortQueryKeys.ASCENDING, SortQueryKeys.JOB_NAME) + every { nodeMock.query } returns jobQueryMock + every { jobQueryMock.sortKeys } returns mutableListOf(SortQueryKeys.ASCENDING, SortQueryKeys.JOB_COMPLETION_DATE) + every { nodeMock.currentSortQueryKeysList } returns mutableListOf(SortQueryKeys.ASCENDING, SortQueryKeys.JOB_COMPLETION_DATE) + + classUnderTest.performQueryUpdateForNode(nodeMock, sortKey) + + assertSoftly { + jobQueryMock.sortKeys shouldContainExactly expectedSortKeys + } + } + } +}) diff --git a/src/test/kotlin/eu/ibagroup/formainframe/explorer/actions/sort/members/MembersSortActionGroupTestSpec.kt b/src/test/kotlin/eu/ibagroup/formainframe/explorer/actions/sort/members/MembersSortActionGroupTestSpec.kt new file mode 100644 index 000000000..69e8bd485 --- /dev/null +++ b/src/test/kotlin/eu/ibagroup/formainframe/explorer/actions/sort/members/MembersSortActionGroupTestSpec.kt @@ -0,0 +1,66 @@ +package org.zowe.explorer.explorer.actions.sort.members + +import com.intellij.openapi.actionSystem.AnActionEvent +import com.intellij.openapi.actionSystem.DataKey +import org.zowe.explorer.explorer.ui.DSMaskNode +import org.zowe.explorer.explorer.ui.FileExplorerView +import org.zowe.explorer.explorer.ui.LibraryNode +import org.zowe.explorer.testutils.WithApplicationShouldSpec +import io.kotest.assertions.assertSoftly +import io.kotest.matchers.shouldBe +import io.kotest.matchers.shouldNotBe +import io.mockk.* + +class MembersSortActionGroupTestSpec : WithApplicationShouldSpec({ + + afterSpec { + clearAllMocks() + unmockkAll() + } + + context("members sort action group spec") { + + val actionEventMock = mockk() + val explorerViewMock = mockk() + // group action to spy + val classUnderTest = spyk(MembersSortActionGroup()) + + should("shouldReturnExplorerView_whenGetExplorerView_givenActionEvent") { + every { actionEventMock.getData(any() as DataKey) } returns explorerViewMock + val actualExplorer = classUnderTest.getSourceView(actionEventMock) + + assertSoftly { + actualExplorer shouldNotBe null + actualExplorer is FileExplorerView + } + } + + should("shouldReturnTrue_whenCheckNode_givenLibraryNode") { + val nodeMock = mockk() + val checkNode = classUnderTest.checkNode(nodeMock) + + assertSoftly { + checkNode shouldBe true + } + } + + should("shouldReturnNull_whenGetExplorerView_givenActionEvent") { + every { actionEventMock.getData(any() as DataKey) } returns null + val actualExplorer = classUnderTest.getSourceView(actionEventMock) + + assertSoftly { + actualExplorer shouldBe null + } + } + + should("shouldReturnFalse_whenCheckNode_givenWrongNode") { + val nodeMock = mockk() + val checkNode = classUnderTest.checkNode(nodeMock) + + assertSoftly { + checkNode shouldBe false + } + } + } + +}) diff --git a/src/test/kotlin/eu/ibagroup/formainframe/explorer/actions/sort/members/MembersSortActionTestSpec.kt b/src/test/kotlin/eu/ibagroup/formainframe/explorer/actions/sort/members/MembersSortActionTestSpec.kt new file mode 100644 index 000000000..4ee78b774 --- /dev/null +++ b/src/test/kotlin/eu/ibagroup/formainframe/explorer/actions/sort/members/MembersSortActionTestSpec.kt @@ -0,0 +1,125 @@ +package org.zowe.explorer.explorer.actions.sort.members + +import com.intellij.openapi.actionSystem.AnActionEvent +import com.intellij.openapi.actionSystem.DataKey +import org.zowe.explorer.dataops.BatchedRemoteQuery +import org.zowe.explorer.dataops.attributes.RemoteDatasetAttributes +import org.zowe.explorer.dataops.fetch.LibraryQuery +import org.zowe.explorer.dataops.sort.SortQueryKeys +import org.zowe.explorer.explorer.ui.* +import org.zowe.explorer.testutils.WithApplicationShouldSpec +import org.zowe.explorer.vfs.MFVirtualFile +import io.kotest.assertions.assertSoftly +import io.kotest.matchers.collections.shouldContainExactly +import io.kotest.matchers.shouldBe +import io.kotest.matchers.shouldNotBe +import io.mockk.* + +class MembersSortActionTestSpec : WithApplicationShouldSpec({ + + afterSpec { + clearAllMocks() + unmockkAll() + } + + context("Members sort action") { + + val actionEventMock = mockk() + val explorerViewMock = mockk() + // action to spy + val classUnderTest = spyk(MembersSortAction()) + + should("returnSourceView_whenGetSourceView_givenActionEvent") { + every { actionEventMock.getData(any() as DataKey) } returns explorerViewMock + + val actualExplorerView = classUnderTest.getSourceView(actionEventMock) + + assertSoftly { + actualExplorerView shouldNotBe null + actualExplorerView is FileExplorerView + } + } + + should("returnNull_whenGetSourceView_givenActionEvent") { + every { actionEventMock.getData(any() as DataKey) } returns null + + val actualExplorerView = classUnderTest.getSourceView(actionEventMock) + + assertSoftly { + actualExplorerView shouldBe null + } + } + + should("returnSourceNode_whenGetSourceNode_givenView") { + val nodeMock = mockk() + val fileMock = mockk() + val attributesMock = mockk() + val myNodesData = mutableListOf(NodeData(nodeMock, fileMock, attributesMock)) + mockkObject(myNodesData) + every { explorerViewMock.mySelectedNodesData } returns myNodesData + + val actualNode = classUnderTest.getSourceNode(explorerViewMock) + + assertSoftly { + actualNode shouldBe nodeMock + } + } + + should("returnNull_whenGetSourceNode_givenView") { + val nodeMock = mockk() + val fileMock = mockk() + val attributesMock = mockk() + val myNodesData = mutableListOf(NodeData(nodeMock, fileMock, attributesMock)) + mockkObject(myNodesData) + every { explorerViewMock.mySelectedNodesData } returns myNodesData + + val actualNode = classUnderTest.getSourceNode(explorerViewMock) + + assertSoftly { + actualNode shouldBe null + } + } + + should("returnTrue_whenShouldEnableSortKeyForNode_givenSelectedNodeAndSortKey") { + val nodeMock = mockk() + val sortKey = SortQueryKeys.MEMBER_NAME + every { nodeMock.currentSortQueryKeysList } returns listOf(SortQueryKeys.MEMBER_NAME, SortQueryKeys.ASCENDING) + + val shouldEnableSortKey = classUnderTest.shouldEnableSortKeyForNode(nodeMock, sortKey) + + assertSoftly { + shouldEnableSortKey shouldBe true + } + } + + should("returnFalse_whenShouldEnableSortKeyForNode_givenSelectedNodeAndSortKey") { + val nodeMock = mockk() + val sortKey = SortQueryKeys.MEMBER_NAME + every { nodeMock.currentSortQueryKeysList } returns listOf() + + val shouldEnableSortKey = classUnderTest.shouldEnableSortKeyForNode(nodeMock, sortKey) + + assertSoftly { + shouldEnableSortKey shouldBe false + } + } + + should("updateQuery_whenPerformQueryUpdateForNode_givenSelectedNodeAndSortKey") { + val batchedQueryMock = mockk>() + val nodeMock = mockk() + val sortKey = SortQueryKeys.MEMBER_NAME + val expectedSortKeys = listOf(SortQueryKeys.ASCENDING, SortQueryKeys.MEMBER_NAME) + every { nodeMock.query } returns batchedQueryMock + every { batchedQueryMock.sortKeys } returns mutableListOf(SortQueryKeys.ASCENDING, SortQueryKeys.MEMBER_MODIFICATION_DATE) + every { nodeMock.currentSortQueryKeysList } returns mutableListOf(SortQueryKeys.ASCENDING, SortQueryKeys.MEMBER_MODIFICATION_DATE) + + classUnderTest.performQueryUpdateForNode(nodeMock, sortKey) + + assertSoftly { + batchedQueryMock.sortKeys shouldContainExactly expectedSortKeys + } + } + + } + +}) diff --git a/src/test/kotlin/eu/ibagroup/formainframe/explorer/actions/sort/uss/UssSortActionGroupTestSpec.kt b/src/test/kotlin/eu/ibagroup/formainframe/explorer/actions/sort/uss/UssSortActionGroupTestSpec.kt new file mode 100644 index 000000000..120a1135e --- /dev/null +++ b/src/test/kotlin/eu/ibagroup/formainframe/explorer/actions/sort/uss/UssSortActionGroupTestSpec.kt @@ -0,0 +1,185 @@ +/* + * This program and the accompanying materials are made available under the terms of the + * Eclipse Public License v2.0 which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-v20.html + * + * SPDX-License-Identifier: EPL-2.0 + * + * Copyright IBA Group 2020 + */ + +package org.zowe.explorer.explorer.actions.sort.uss + +import com.intellij.openapi.actionSystem.ActionUpdateThread +import com.intellij.openapi.actionSystem.AnActionEvent +import com.intellij.openapi.actionSystem.DataKey +import com.intellij.openapi.actionSystem.Presentation +import org.zowe.explorer.config.connect.ConnectionConfig +import org.zowe.explorer.dataops.UnitRemoteQueryImpl +import org.zowe.explorer.dataops.attributes.RemoteDatasetAttributes +import org.zowe.explorer.dataops.attributes.RemoteUssAttributes +import org.zowe.explorer.dataops.fetch.UssQuery +import org.zowe.explorer.explorer.ui.* +import org.zowe.explorer.testutils.WithApplicationShouldSpec +import org.zowe.explorer.vfs.MFVirtualFile +import io.kotest.assertions.assertSoftly +import io.kotest.matchers.shouldBe +import io.kotest.matchers.shouldNotBe +import io.mockk.* +import javax.swing.tree.TreePath + +class UssSortActionGroupTestSpec : WithApplicationShouldSpec({ + + afterSpec { + clearAllMocks() + unmockkAll() + } + + context("uss sort action group spec") { + + // group action to spy + val classUnderTest = spyk(UssSortActionGroup()) + + val mockedActionEvent = mockk() + val selectionPath = mockk() + val mockedFileExplorerView = mockk() + every { mockedActionEvent.getExplorerView() } returns mockedFileExplorerView + every { mockedFileExplorerView.myTree } returns mockk() + every { mockedFileExplorerView.myTree.selectionPath } returns selectionPath + + // Presentation + val presentation = Presentation() + every { mockedActionEvent.presentation } returns presentation + + // Target UssDirNode + Query for test + val mockedMFVirtualFile = mockk() + val mockedUssDirNode = mockk() + val mockedUssRemoteAttributes = mockk() + val mockedUssQuery = mockk>() + every { mockedUssDirNode.virtualFile } returns mockedMFVirtualFile + every { mockedUssDirNode.query } returns mockedUssQuery + + // NodeData for test + val mockedNodeDataForTest = NodeData(mockedUssDirNode, mockedMFVirtualFile, mockedUssRemoteAttributes) + mockkObject(mockedNodeDataForTest) + + should("shouldReturnExplorerView_whenGetExplorerView_givenActionEvent") { + every { mockedActionEvent.getData(any() as DataKey) } returns mockedFileExplorerView + val actualExplorer = classUnderTest.getSourceView(mockedActionEvent) + + assertSoftly { + actualExplorer shouldNotBe null + actualExplorer is FileExplorerView + } + } + + should("shouldReturnTrue_whenCheckNode_givenUssDirNode") { + val nodeMock = mockk() + val checkNode = classUnderTest.checkNode(nodeMock) + + assertSoftly { + checkNode shouldBe true + } + } + + should("shouldReturnNull_whenGetExplorerView_givenActionEvent") { + every { mockedActionEvent.getData(any() as DataKey) } returns null + val actualExplorer = classUnderTest.getSourceView(mockedActionEvent) + + assertSoftly { + actualExplorer shouldBe null + } + } + + should("shouldReturnFalse_whenCheckNode_givenWrongNode") { + val nodeMock = mockk() + val checkNode = classUnderTest.checkNode(nodeMock) + + assertSoftly { + checkNode shouldBe false + } + } + + should("is visible from context menu if file explorer view is null") { + var isVisible = true + every { mockedActionEvent.getExplorerView() } answers { + isVisible = false + null + } + + classUnderTest.update(mockedActionEvent) + assertSoftly { + isVisible shouldBe false + } + } + + should("is visible from context menu if file explorer view is not null and selected node is not UssDirNode") { + var isVisible = true + val mockedNodeDataNotUssForTest = + NodeData(mockk(), mockk(), mockk()) + every { mockedFileExplorerView.mySelectedNodesData } answers { + isVisible = false + listOf(mockedNodeDataNotUssForTest) + } + every { mockedFileExplorerView.myTree.isExpanded(selectionPath) } returns true + every { mockedActionEvent.getExplorerView() } returns mockedFileExplorerView + + classUnderTest.update(mockedActionEvent) + assertSoftly { + isVisible shouldBe false + } + } + + should("is visible from context menu if file explorer view is not null and selected node is UssDirNode and path is expanded") { + var isVisible = false + every { mockedFileExplorerView.mySelectedNodesData } returns listOf(mockedNodeDataForTest) + every { mockedFileExplorerView.myTree.isExpanded(selectionPath) } answers { + isVisible = true + true + } + + classUnderTest.update(mockedActionEvent) + assertSoftly { + isVisible shouldBe true + } + } + + should("is visible from context menu if file explorer view is not null and selected node is UssDirNode and path is not expanded") { + var isVisible = true + every { mockedFileExplorerView.mySelectedNodesData } returns listOf(mockedNodeDataForTest) + every { mockedFileExplorerView.myTree.isExpanded(selectionPath) } answers { + isVisible = false + false + } + + classUnderTest.update(mockedActionEvent) + assertSoftly { + isVisible shouldBe false + } + } + + should("is visible from context menu if file explorer view is not null and selectedNodes size > 1") { + var isVisible = true + every { mockedFileExplorerView.mySelectedNodesData } answers { + isVisible = false + listOf(mockedNodeDataForTest, mockedNodeDataForTest) + } + + classUnderTest.update(mockedActionEvent) + assertSoftly { + isVisible shouldBe false + } + } + + should("return EDT thread_whenGetActionUpdateThread_givenNothing") { + //given + + //when + val thread = classUnderTest.actionUpdateThread + //then + assertSoftly { + thread shouldBe ActionUpdateThread.EDT + } + } + } +}) diff --git a/src/test/kotlin/eu/ibagroup/formainframe/explorer/actions/sort/uss/UssSortActionTestSpec.kt b/src/test/kotlin/eu/ibagroup/formainframe/explorer/actions/sort/uss/UssSortActionTestSpec.kt new file mode 100644 index 000000000..8bba6789c --- /dev/null +++ b/src/test/kotlin/eu/ibagroup/formainframe/explorer/actions/sort/uss/UssSortActionTestSpec.kt @@ -0,0 +1,311 @@ +/* + * This program and the accompanying materials are made available under the terms of the + * Eclipse Public License v2.0 which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-v20.html + * + * SPDX-License-Identifier: EPL-2.0 + * + * Copyright IBA Group 2020 + */ + +package org.zowe.explorer.explorer.actions.sort.uss + +import com.intellij.openapi.actionSystem.AnActionEvent +import com.intellij.openapi.actionSystem.DataKey +import com.intellij.openapi.actionSystem.impl.SimpleDataContext +import org.zowe.explorer.config.connect.ConnectionConfig +import org.zowe.explorer.dataops.UnitRemoteQueryImpl +import org.zowe.explorer.dataops.attributes.RemoteDatasetAttributes +import org.zowe.explorer.dataops.attributes.RemoteUssAttributes +import org.zowe.explorer.dataops.fetch.UssQuery +import org.zowe.explorer.dataops.sort.SortQueryKeys +import org.zowe.explorer.explorer.actions.sort.SortAction +import org.zowe.explorer.explorer.ui.* +import org.zowe.explorer.testutils.WithApplicationShouldSpec +import org.zowe.explorer.vfs.MFVirtualFile +import io.kotest.assertions.assertSoftly +import io.kotest.assertions.throwables.shouldThrowExactly +import io.kotest.matchers.collections.shouldContainExactly +import io.kotest.matchers.shouldBe +import io.kotest.matchers.shouldNotBe +import io.mockk.* + +class UssSortActionTestSpec : WithApplicationShouldSpec ({ + + afterSpec { + clearAllMocks() + unmockkAll() + } + + context("USS sort action") { + + mockkObject(SortAction.Companion) + every { SortAction.runRefreshAction(any()) } just Runs + + // action to spy + val classUnderTest = spyk(UssSortAction()) + + val mockedActionEvent = mockk() + + context("common spec") { + + val mockedFileExplorerView = mockk() + + // Target UssDirNode + Query for test + val mockedMFVirtualFile = mockk() + val mockedUssDirNode = mockk() + val mockedUssRemoteAttributes = mockk() + val mockedUssQuery = mockk>() + every { mockedUssDirNode.virtualFile } returns mockedMFVirtualFile + every { mockedUssDirNode.query } returns mockedUssQuery + every { mockedUssQuery.sortKeys } returns mutableListOf() + + // NodeData for test + val mockedNodeDataForTest = NodeData(mockedUssDirNode, mockedMFVirtualFile, mockedUssRemoteAttributes) + mockkObject(mockedNodeDataForTest) + + // Common config for test + every { mockedActionEvent.getExplorerView() } returns mockedFileExplorerView + + context("misc") { + + should("returnSourceView_whenGetSourceView_givenActionEvent") { + every { mockedActionEvent.getData(any() as DataKey) } returns mockedFileExplorerView + + val actualExplorerView = classUnderTest.getSourceView(mockedActionEvent) + + assertSoftly { + actualExplorerView shouldNotBe null + actualExplorerView is FileExplorerView + } + } + + should("returnNull_whenGetSourceView_givenActionEvent") { + every { mockedActionEvent.getData(any() as DataKey) } returns null + + val actualExplorerView = classUnderTest.getSourceView(mockedActionEvent) + + assertSoftly { + actualExplorerView shouldBe null + } + } + + should("returnSourceNode_whenGetSourceNode_givenView") { + val nodeMock = mockk() + val fileMock = mockk() + val attributesMock = mockk() + val myNodesData = mutableListOf(NodeData(nodeMock, fileMock, attributesMock)) + mockkObject(myNodesData) + every { mockedFileExplorerView.mySelectedNodesData } returns myNodesData + + val actualNode = classUnderTest.getSourceNode(mockedFileExplorerView) + + assertSoftly { + actualNode shouldBe nodeMock + } + } + + should("returnNull_whenGetSourceNode_givenView") { + val nodeMock = mockk() + val fileMock = mockk() + val attributesMock = mockk() + val myNodesData = mutableListOf(NodeData(nodeMock, fileMock, attributesMock)) + mockkObject(myNodesData) + every { mockedFileExplorerView.mySelectedNodesData } returns myNodesData + + val actualNode = classUnderTest.getSourceNode(mockedFileExplorerView) + + assertSoftly { + actualNode shouldBe null + } + } + + should("returnTrue_whenShouldEnableSortKeyForNode_givenSelectedNodeAndSortKey") { + val nodeMock = mockk() + val sortKey = SortQueryKeys.FILE_NAME + every { nodeMock.currentSortQueryKeysList } returns listOf(SortQueryKeys.FILE_NAME, SortQueryKeys.ASCENDING) + + val shouldEnableSortKey = classUnderTest.shouldEnableSortKeyForNode(nodeMock, sortKey) + + assertSoftly { + shouldEnableSortKey shouldBe true + } + } + + should("returnFalse_whenShouldEnableSortKeyForNode_givenSelectedNodeAndSortKey") { + val nodeMock = mockk() + val sortKey = SortQueryKeys.FILE_NAME + every { nodeMock.currentSortQueryKeysList } returns listOf() + + val shouldEnableSortKey = classUnderTest.shouldEnableSortKeyForNode(nodeMock, sortKey) + + assertSoftly { + shouldEnableSortKey shouldBe false + } + } + + should("updateQuery_whenPerformQueryUpdateForNode_givenSelectedNodeAndSortKey") { + val ussQueryMock = mockk>() + val nodeMock = mockk() + val sortKey = SortQueryKeys.FILE_NAME + val expectedSortKeys = listOf(SortQueryKeys.ASCENDING, SortQueryKeys.FILE_NAME) + every { nodeMock.query } returns ussQueryMock + every { ussQueryMock.sortKeys } returns mutableListOf(SortQueryKeys.ASCENDING, SortQueryKeys.FILE_MODIFICATION_DATE) + every { nodeMock.currentSortQueryKeysList } returns mutableListOf(SortQueryKeys.ASCENDING, SortQueryKeys.FILE_MODIFICATION_DATE) + + classUnderTest.performQueryUpdateForNode(nodeMock, sortKey) + + assertSoftly { + ussQueryMock.sortKeys shouldContainExactly expectedSortKeys + } + } + } + + context("isSelected") { + + should("returnFalse_whenIsSelected_givenExplorerNull") { + every { mockedActionEvent.getData(any() as DataKey) } returns null + val isSelected = classUnderTest.isSelected(mockedActionEvent) + assertSoftly { + isSelected shouldBe false + } + } + + should("returnFalse_whenIsSelected_givenNullTemplateText") { + every { mockedActionEvent.getData(any() as DataKey) } returns mockedFileExplorerView + every { classUnderTest.templateText } returns null + val isSelected = classUnderTest.isSelected(mockedActionEvent) + assertSoftly { + isSelected shouldBe false + } + } + + should("returnFalse_whenIsSelected_givenNotUssDirNode") { + every { classUnderTest.templateText } returns "File Name" + val mockedNodeDataNotUssForTest = + NodeData(mockk(), mockk(), mockk()) + every { mockedFileExplorerView.mySelectedNodesData } answers { + listOf(mockedNodeDataNotUssForTest) + } + val isSelected = classUnderTest.isSelected(mockedActionEvent) + assertSoftly { + isSelected shouldBe false + } + } + + should("returnFalse_whenIsSelected_givenNodeWithNoKeysSpecified") { + every { mockedFileExplorerView.mySelectedNodesData } returns listOf(mockedNodeDataForTest) + every { mockedUssDirNode.currentSortQueryKeysList } answers { + mutableListOf() + } + val isSelected = classUnderTest.isSelected(mockedActionEvent) + assertSoftly { + isSelected shouldBe false + } + } + + should("returnTrue_whenIsSelected_givenValidDataAndSortKey") { + every { mockedUssDirNode.currentSortQueryKeysList } answers { + mutableListOf(SortQueryKeys.FILE_NAME, SortQueryKeys.ASCENDING) + } + val isSelected = classUnderTest.isSelected(mockedActionEvent) + assertSoftly { + isSelected shouldBe true + } + } + } + + context("setSelected") { + + should("return_whenSetSelected_givenExplorerNull") { + // given + var setSelected = true + every { mockedActionEvent.getData(any() as DataKey) } answers { + setSelected = false + null + } + + // when + classUnderTest.setSelected(mockedActionEvent, true) + + // then + assertSoftly { setSelected shouldBe false } + } + + should("throwException_whenSetSelected_givenNullTemplateText") { + // given + every { mockedActionEvent.getData(any() as DataKey) } returns mockedFileExplorerView + every { classUnderTest.templateText } returns null + + // when + val exception = shouldThrowExactly { classUnderTest.setSelected(mockedActionEvent, true) } + + // then + assertSoftly { + exception shouldNotBe null + exception.message shouldBe "Sort key for the selected action was not found." + } + } + + should("return_whenSetSelected_givenNotUssDirNode") { + // given + var setSelected = true + every { classUnderTest.isSelected(any()) } returns false + val mockedNodeDataNotUssForTest = + NodeData(mockk(), mockk(), mockk()) + every { mockedFileExplorerView.mySelectedNodesData } answers { + setSelected = false + listOf(mockedNodeDataNotUssForTest) + } + + // when + classUnderTest.setSelected(mockedActionEvent, true) + + // then + assertSoftly { + setSelected shouldBe false + } + } + + should("shouldSetSelected_whenSetSelected_givenValidSortKey") { + // given + val dataContext = mockk() + val expectedSortKeys = listOf(SortQueryKeys.ASCENDING, SortQueryKeys.FILE_TYPE) + every { classUnderTest.templateText } returns "File Type" + every { classUnderTest.isSelected(any()) } returns false + every { mockedFileExplorerView.mySelectedNodesData } returns listOf(mockedNodeDataForTest) + every { mockedUssDirNode.currentSortQueryKeysList } returns mutableListOf(SortQueryKeys.FILE_NAME, SortQueryKeys.ASCENDING) + every { mockedActionEvent.place } returns "Place" + every { mockedActionEvent.dataContext } returns dataContext + + // when + classUnderTest.setSelected(mockedActionEvent, true) + + // then + assertSoftly { + mockedUssQuery.sortKeys shouldContainExactly expectedSortKeys + } + } + + should("return_whenSetSelected_givenAlreadySelectedSortKey") { + // + clearMocks(classUnderTest, mockedActionEvent, + answers = false, recordedCalls = true, childMocks = false, verificationMarks = true, exclusionRules = false) + var setSelected = true + every { classUnderTest.templateText } returns "File Name" + every { classUnderTest.isSelected(any()) } answers { + setSelected = false + true + } + + // when + classUnderTest.setSelected(mockedActionEvent, true) + + // then + verify(exactly = 1) { classUnderTest.isSelected(mockedActionEvent) } + assertSoftly { setSelected shouldBe false } + } + } + } + } +}) diff --git a/src/test/kotlin/org/zowe/explorer/dataops/DatasetFileFetchProviderTestSpec.kt b/src/test/kotlin/org/zowe/explorer/dataops/DatasetFileFetchProviderTestSpec.kt new file mode 100644 index 000000000..a7ad8d22f --- /dev/null +++ b/src/test/kotlin/org/zowe/explorer/dataops/DatasetFileFetchProviderTestSpec.kt @@ -0,0 +1,231 @@ +/* + * This program and the accompanying materials are made available under the terms of the + * Eclipse Public License v2.0 which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-v20.html + * + * SPDX-License-Identifier: EPL-2.0 + * + * Copyright IBA Group 2020 + */ + +package org.zowe.explorer.dataops + +import com.intellij.openapi.application.ApplicationManager +import com.intellij.openapi.progress.ProgressIndicator +import com.intellij.openapi.vfs.VirtualFile +import com.intellij.testFramework.LightProjectDescriptor +import com.intellij.testFramework.fixtures.IdeaTestFixtureFactory +import com.intellij.testFramework.fixtures.impl.LightTempDirTestFixtureImpl +import org.zowe.explorer.api.ZosmfApi +import org.zowe.explorer.config.connect.ConnectionConfig +import org.zowe.explorer.config.connect.authToken +import org.zowe.explorer.config.ws.DSMask +import org.zowe.explorer.dataops.attributes.AttributesService +import org.zowe.explorer.dataops.attributes.FileAttributes +import org.zowe.explorer.dataops.attributes.MaskedRequester +import org.zowe.explorer.dataops.attributes.RemoteDatasetAttributes +import org.zowe.explorer.dataops.attributes.RemoteDatasetAttributesService +import org.zowe.explorer.dataops.fetch.DatasetFileFetchProvider +import org.zowe.explorer.testutils.testServiceImpl.TestDataOpsManagerImpl +import org.zowe.explorer.testutils.testServiceImpl.TestZosmfApiImpl +import org.zowe.explorer.utils.cancelByIndicator +import org.zowe.explorer.utils.service +import org.zowe.explorer.vfs.MFVirtualFile +import io.kotest.assertions.assertSoftly +import io.kotest.core.spec.style.ShouldSpec +import io.kotest.matchers.collections.shouldHaveSize +import io.kotest.matchers.shouldBe +import io.mockk.Runs +import io.mockk.clearAllMocks +import io.mockk.every +import io.mockk.just +import io.mockk.mockk +import io.mockk.spyk +import io.mockk.unmockkAll +import org.zowe.kotlinsdk.DataAPI +import org.zowe.kotlinsdk.DataSetsList +import org.zowe.kotlinsdk.Dataset +import org.zowe.kotlinsdk.DatasetOrganization +import org.zowe.kotlinsdk.HasMigrated +import org.zowe.kotlinsdk.XIBMAttr +import retrofit2.Call +import retrofit2.Response + +class DatasetFileFetchProviderTestSpec : ShouldSpec({ + + beforeSpec { + // FIXTURE SETUP TO HAVE ACCESS TO APPLICATION INSTANCE + val factory = IdeaTestFixtureFactory.getFixtureFactory() + val projectDescriptor = LightProjectDescriptor.EMPTY_PROJECT_DESCRIPTOR + val fixtureBuilder = factory.createLightFixtureBuilder(projectDescriptor, "for-mainframe") + val fixture = fixtureBuilder.fixture + val myFixture = IdeaTestFixtureFactory.getFixtureFactory().createCodeInsightFixture( + fixture, + LightTempDirTestFixtureImpl(true) + ) + myFixture.setUp() + } + + afterSpec { + clearAllMocks() + } + + context("dataops module: fetch") { + + context("DatasetFileFetchProvider common") { + val mockedConnectionConfig = mockk() + every { mockedConnectionConfig.authToken } returns "auth_token" + every { mockedConnectionConfig.url } returns "test_url" + val mockedQuery = mockk>() + val mockedRequest = mockk() + every { mockedRequest.mask } returns "TESTMASK" + every { mockedRequest.volser } returns "TESTVOL" + + val progressMockk = mockk() + every { progressMockk.fraction = any() as Double } just Runs + + every { mockedQuery.request } returns mockedRequest + every { mockedQuery.connectionConfig } returns mockedConnectionConfig + + val dataset1 = mockk() + val dataset2 = mockk() + val dataset3 = mockk() + every { dataset1.name } returns "DATASET1" + every { dataset2.name } returns "DATASET2" + every { dataset3.name } returns "DATASET3" + every { dataset1.migrated } returns HasMigrated.NO + every { dataset2.migrated } returns HasMigrated.NO + every { dataset3.migrated } returns HasMigrated.NO + every { dataset1.datasetOrganization } returns DatasetOrganization.PS + every { dataset2.datasetOrganization } returns DatasetOrganization.PS + every { dataset3.datasetOrganization } returns DatasetOrganization.PO + val dataSetsList = mockk() + every { dataSetsList.items } returns mutableListOf(dataset1, dataset2, dataset3) + every { dataSetsList.totalRows } returns 3 + val mockedCall = mockk>() + val mockedResponse = mockk>() + every { mockedCall.execute() } returns mockedResponse + every { mockedResponse.body() } returns dataSetsList + + val zosmfApi = ApplicationManager.getApplication().service() as TestZosmfApiImpl + zosmfApi.testInstance = mockk() + val mockedApi = mockk() + every { zosmfApi.testInstance.getApi(DataAPI::class.java, mockedConnectionConfig) } returns mockedApi + every { + mockedApi.listDataSets( + authorizationToken = any() as String, + dsLevel = any() as String, + volser = any() as String, + xIBMAttr = any() as XIBMAttr, + xIBMMaxItems = any() as Int, + start = any() as String + ) + } returns mockedCall + every { + mockedApi.listDataSets( + authorizationToken = any() as String, + dsLevel = any() as String, + volser = any() as String, + xIBMAttr = any() as XIBMAttr, + xIBMMaxItems = any() as Int, + start = any() as String + ).cancelByIndicator(progressMockk) + } returns mockedCall + + val dataOpsManagerService = + ApplicationManager.getApplication().service() as TestDataOpsManagerImpl + val componentManager = dataOpsManagerService.componentManager + + // needed for cleanupUnusedFile test + val mockedVirtualFile = mockk() + val mockedFileAttributes = mockk() + val mockedAttributesService = mockk() + val mockedMaskedRequester = mockk() + val requesters = mutableListOf(mockedMaskedRequester) + + afterEach { unmockkAll() } + + val datasetFileFetchProviderForTest = + spyk(DatasetFileFetchProvider(dataOpsManagerService), recordPrivateCalls = true) + + should("dataset fetch provider fetchResponse test") { + val fetchResponseMethodRef = + datasetFileFetchProviderForTest::class.java.declaredMethods.first { it.name == "fetchResponse" } + fetchResponseMethodRef.trySetAccessible() + every { mockedResponse.isSuccessful } returns true + + val datasetAttributes = + (fetchResponseMethodRef.invoke(datasetFileFetchProviderForTest, mockedQuery, progressMockk) as Collection<*>) + + assertSoftly { + datasetAttributes shouldHaveSize dataSetsList.items.size + } + } + + should("cleanup unused file if connection config of the query is the same as for dataset file") { + var cleanupPerformed = false + val cleanupUnusedFileMethodRef = + datasetFileFetchProviderForTest::class.java.declaredMethods.first { it.name == "cleanupUnusedFile" } + cleanupUnusedFileMethodRef.trySetAccessible() + dataOpsManagerService.testInstance = object : TestDataOpsManagerImpl(componentManager) { + override fun getAttributesService( + attributesClass: Class, + vFileClass: Class + ): AttributesService { + return mockedAttributesService as AttributesService + } + } + + every { mockedAttributesService.getAttributes(mockedVirtualFile) } returns mockedFileAttributes + every { mockedAttributesService.clearAttributes(mockedVirtualFile) } just Runs + + every { mockedMaskedRequester.connectionConfig } returns mockedConnectionConfig + every { mockedMaskedRequester.queryVolser } returns "TESTVOL" + every { mockedFileAttributes.requesters } returns requesters + + every { mockedVirtualFile.delete(any() as DatasetFileFetchProvider) } answers { + cleanupPerformed = true + } + + cleanupUnusedFileMethodRef.invoke(datasetFileFetchProviderForTest, mockedVirtualFile, mockedQuery) + + assertSoftly { + cleanupPerformed shouldBe true + } + } + + should("cleanup unused file if connection config of the query is the same as for dataset file") { + var cleanupPerformed = false + val cleanupUnusedFileMethodRef = + datasetFileFetchProviderForTest::class.java.declaredMethods.first { it.name == "cleanupUnusedFile" } + cleanupUnusedFileMethodRef.trySetAccessible() + + every { mockedAttributesService.getAttributes(mockedVirtualFile) } returns mockedFileAttributes + every { mockedAttributesService.clearAttributes(mockedVirtualFile) } just Runs + + every { mockedMaskedRequester.connectionConfig } returns mockedConnectionConfig + every { mockedMaskedRequester.queryVolser } returns "ANOTHER" + every { mockedFileAttributes.requesters } returns requesters + every { + mockedAttributesService.updateAttributes( + mockedVirtualFile, + any() as RemoteDatasetAttributes.() -> Unit + ) + } answers { + cleanupPerformed = true + secondArg Unit>().invoke(mockedFileAttributes) + } + + every { mockedVirtualFile.delete(any() as DatasetFileFetchProvider) } answers { + cleanupPerformed = true + } + + cleanupUnusedFileMethodRef.invoke(datasetFileFetchProviderForTest, mockedVirtualFile, mockedQuery) + + assertSoftly { + cleanupPerformed shouldBe true + } + } + } + } +}) diff --git a/src/test/kotlin/org/zowe/explorer/dataops/FetchTestSpec.kt b/src/test/kotlin/org/zowe/explorer/dataops/FetchTestSpec.kt deleted file mode 100644 index f89e0d53a..000000000 --- a/src/test/kotlin/org/zowe/explorer/dataops/FetchTestSpec.kt +++ /dev/null @@ -1,23 +0,0 @@ -/* - * This program and the accompanying materials are made available under the terms of the - * Eclipse Public License v2.0 which accompanies this distribution, and is available at - * https://www.eclipse.org/legal/epl-v20.html - * - * SPDX-License-Identifier: EPL-2.0 - * - * Copyright IBA Group 2020 - */ - -package org.zowe.explorer.dataops - -import io.kotest.core.spec.style.ShouldSpec - -class FetchTestSpec : ShouldSpec({ - context("dataops module: fetch") { - // SpoolFileFetchProvider.reload - should("reload files cache") {} - should("reload files cache with failure") {} - // DatasetFileFetchProvider.cleanupUnusedFile - should("clean up files attributes") {} - } -}) diff --git a/src/test/kotlin/org/zowe/explorer/dataops/JobFetchHelperTestSpec.kt b/src/test/kotlin/org/zowe/explorer/dataops/JobFetchHelperTestSpec.kt new file mode 100644 index 000000000..0f4f54b98 --- /dev/null +++ b/src/test/kotlin/org/zowe/explorer/dataops/JobFetchHelperTestSpec.kt @@ -0,0 +1,223 @@ +/* + * This program and the accompanying materials are made available under the terms of the + * Eclipse Public License v2.0 which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-v20.html + * + * SPDX-License-Identifier: EPL-2.0 + * + * Copyright IBA Group 2020 + */ + +package org.zowe.explorer.dataops + +import org.zowe.explorer.config.connect.ConnectionConfig +import org.zowe.explorer.config.ws.JobsFilter +import org.zowe.explorer.dataops.attributes.JobsRequester +import org.zowe.explorer.dataops.attributes.RemoteJobAttributes +import org.zowe.explorer.dataops.fetch.JobFetchHelper +import org.zowe.explorer.dataops.log.JobLogFetcher +import org.zowe.explorer.dataops.log.JobProcessInfo +import org.zowe.explorer.utils.asMutableList +import io.kotest.assertions.assertSoftly +import io.kotest.core.spec.style.ShouldSpec +import io.kotest.matchers.shouldBe +import io.kotest.matchers.shouldNotBe +import io.mockk.* +import org.zowe.kotlinsdk.Job + +class JobFetchHelperTestSpec : ShouldSpec({ + context("dataops module: fetch") { + + context("JobFetchHelper common") { + + val mockedConnectionConfig = mockk() + + val mockedJobFilter = mockk() + every { mockedJobFilter.jobId } returns "TEST" + every { mockedJobFilter.owner } returns "ZOSMFAD" + every { mockedJobFilter.prefix } returns "TEST*" + + val jobQuery = mockk>() + every { jobQuery.connectionConfig } returns mockedConnectionConfig + every { jobQuery.request } returns mockedJobFilter + + val jobInfoForTest = Job("TSU06062", "TESTJOB", "TEST1", + "ZOSMFAD", Job.Status.OUTPUT, Job.JobType.JOB, null, "CC=00", + "test_url", "test_url", null, 1, "phase", + emptyList(), null, null, null, null, null, null + ) + val mockedJobAttributesForTest = mockk() + every { mockedJobAttributesForTest.jobInfo } returns jobInfoForTest + every { mockedJobAttributesForTest.url } returns jobInfoForTest.url + every { mockedConnectionConfig.url } returns jobInfoForTest.url + every { mockedJobAttributesForTest.requesters } returns JobsRequester(jobQuery.connectionConfig, jobQuery.request).asMutableList() + + var updatedJobAttributes: RemoteJobAttributes? + + beforeEach { updatedJobAttributes = null } + afterEach { unmockkAll() } + + context("JobFetchHelper create instance and perform tests") { + + should("run job fetch helper if fetch spool file returns empty logs") { + + val jobFetchHelperForTest = spyk(JobFetchHelper(jobQuery, mockedJobAttributesForTest), recordPrivateCalls = true) + val jobFetcherField = jobFetchHelperForTest::class.java.getDeclaredField("jobLogFetcher") + jobFetcherField.isAccessible = true + val jobFetcher = jobFetcherField.get(jobFetchHelperForTest) as JobLogFetcher + mockkObject(jobFetcher) + + val emptySpoolContent = emptyArray() + every { jobFetcher.fetchLogsBySpoolId(any() as JobProcessInfo, any() as Int) } returns emptySpoolContent + + jobFetchHelperForTest.start() + while(true) { + if (!jobFetchHelperForTest.isAlive) { + updatedJobAttributes = jobFetchHelperForTest.getUpdatedJobAttributes() + break + } + } + assertSoftly { + updatedJobAttributes?.jobInfo?.execStarted?.trim() shouldBe "" + updatedJobAttributes?.jobInfo?.execEnded?.trim() shouldBe "JCL NOT AVAILABLE" + } + } + + should("run job fetch helper if fetch spool file returns started/ended date and time") { + + val jobFetchHelperForTest = spyk(JobFetchHelper(jobQuery, mockedJobAttributesForTest), recordPrivateCalls = true) + val jobFetcherField = jobFetchHelperForTest::class.java.getDeclaredField("jobLogFetcher") + jobFetcherField.isAccessible = true + val jobFetcher = jobFetcherField.get(jobFetchHelperForTest) as JobLogFetcher + mockkObject(jobFetcher) + + val spoolContent = arrayOf( + "J E S 2 J O B L O G -- S Y S T E M S 0 W 1 -- N O D E S 0 W 1\n" + + "0\n" + + "19.45.23 TSU06062 ---- TUESDAY, 20 JUN 2023 ----\n" + + "19.45.23 TSU06062 HASP373 ZOSMFAD STARTED\n" + + "19.45.23 TSU06062 IEF125I ZOSMFAD - LOGGED ON - TIME=19.45.23\n" + + "20.09.28 TSU06062 BPXP018I THREAD 2037180000000000, IN PROCESS 83952040, ENDED 856\n" + + "856 WITHOUT BEING UNDUBBED WITH COMPLETION CODE 40222000\n" + + "856 , AND REASON CODE 00000000.\n" + + "20.09.29 TSU06062 IEF450I ZOSMFAD IZUFPROC IZUFPROC - ABEND=S222 U0000 REASON=00000000 858\n" + + "858 TIME=20.09.29\n" + + "20.09.29 TSU06062 HASP395 TESTJOB ENDED - ABEND=S222\n" + + "0------ JES2 JOB STATISTICS ------\n" + + "- 20 JUN 2023 JOB EXECUTION DATE\n" + + " - 3 CARDS READ\n" + + " - 148 SYSOUT PRINT RECORDS\n" + + " - 0 SYSOUT PUNCH RECORDS\n" + + " - 13 SYSOUT SPOOL KBYTES\n" + + " - 24.09 MINUTES EXECUTION TIME" + ) + every { jobFetcher.fetchLogsBySpoolId(any() as JobProcessInfo, any() as Int) } returns spoolContent + + jobFetchHelperForTest.start() + while(true) { + if (!jobFetchHelperForTest.isAlive) { + updatedJobAttributes = jobFetchHelperForTest.getUpdatedJobAttributes() + break + } + } + assertSoftly { + updatedJobAttributes?.jobInfo?.execStarted?.trim() shouldBe "20 JUN 2023 19.45.23" + updatedJobAttributes?.jobInfo?.execEnded?.trim() shouldBe "20 JUN 2023 20.09.29" + } + } + + should("run job fetch helper if fetch spool file returns started/ended date and time if ended date was not found") { + + val jobFetchHelperForTest = spyk(JobFetchHelper(jobQuery, mockedJobAttributesForTest), recordPrivateCalls = true) + val jobFetcherField = jobFetchHelperForTest::class.java.getDeclaredField("jobLogFetcher") + jobFetcherField.isAccessible = true + val jobFetcher = jobFetcherField.get(jobFetchHelperForTest) as JobLogFetcher + mockkObject(jobFetcher) + + val spoolContent = arrayOf( + "J E S 2 J O B L O G -- S Y S T E M S 0 W 1 -- N O D E S 0 W 1\n" + + "0\n" + + "19.45.23 TSU06061 ---- TUESDAY, 20 JUN 2023 ----\n" + + "19.45.23 TSU06062 HASP373 TESTJOB STARTED\n" + + "19.45.23 TSU06062 IEF125I ZOSMFAD - LOGGED ON - TIME=19.45.23\n" + + "20.09.28 TSU06062 BPXP018I THREAD 2037180000000000, IN PROCESS 83952040, ENDED 856\n" + + "856 WITHOUT BEING UNDUBBED WITH COMPLETION CODE 40222000\n" + + "856 , AND REASON CODE 00000000.\n" + + "20.09.29 TSU06062 IEF450I ZOSMFAD IZUFPROC IZUFPROC - ABEND=S222 U0000 REASON=00000000 858\n" + + "858 TIME=20.09.29\n" + + "20.09.29 TSU06062 HASP395 TESTJOB ENDED - ABEND=S222\n" + + "0------ JES2 JOB STATISTICS ------\n" + + "- 20 JUN 2023 JOB\n" + + " - 3 CARDS READ\n" + + " - 148 SYSOUT PRINT RECORDS\n" + + " - 0 SYSOUT PUNCH RECORDS\n" + + " - 13 SYSOUT SPOOL KBYTES\n" + + " - 24.09 MINUTES EXECUTION TIME" + ) + every { jobFetcher.fetchLogsBySpoolId(any() as JobProcessInfo, any() as Int) } returns spoolContent + + jobFetchHelperForTest.start() + while(true) { + if (!jobFetchHelperForTest.isAlive) { + updatedJobAttributes = jobFetchHelperForTest.getUpdatedJobAttributes() + break + } + } + assertSoftly { + updatedJobAttributes?.jobInfo?.execStarted?.trim() shouldBe "20 JUN 2023 19.45.23" + updatedJobAttributes?.jobInfo?.execEnded?.trim() shouldBe "20 JUN 2023 20.09.29" + } + } + + should("run job fetch helper if fetch spool file returns no date and time in it") { + + val jobFetchHelperForTest = spyk(JobFetchHelper(jobQuery, mockedJobAttributesForTest), recordPrivateCalls = true) + val jobFetcherField = jobFetchHelperForTest::class.java.getDeclaredField("jobLogFetcher") + jobFetcherField.isAccessible = true + val jobFetcher = jobFetcherField.get(jobFetchHelperForTest) as JobLogFetcher + mockkObject(jobFetcher) + + val spoolContent = arrayOf("J E S 2 J O B L O G -- S Y S T E M S 0 W 1 -- N O D E S 0 W 1") + every { jobFetcher.fetchLogsBySpoolId(any() as JobProcessInfo, any() as Int) } returns spoolContent + + jobFetchHelperForTest.start() + while(true) { + if (!jobFetchHelperForTest.isAlive) { + updatedJobAttributes = jobFetchHelperForTest.getUpdatedJobAttributes() + break + } + } + assertSoftly { + updatedJobAttributes?.jobInfo?.execStarted?.trim() shouldBe "" + updatedJobAttributes?.jobInfo?.execEnded?.trim() shouldBe "" + } + } + + should("run job fetch helper if fetch spool file throws an exception") { + + val exception: Throwable? + val jobFetchHelperForTest = spyk(JobFetchHelper(jobQuery, mockedJobAttributesForTest), recordPrivateCalls = true) + val jobFetcherField = jobFetchHelperForTest::class.java.getDeclaredField("jobLogFetcher") + jobFetcherField.isAccessible = true + val jobFetcher = jobFetcherField.get(jobFetchHelperForTest) as JobLogFetcher + mockkObject(jobFetcher) + + every { jobFetcher.fetchLogsBySpoolId(any() as JobProcessInfo, any() as Int) } answers { + throw IllegalArgumentException("TEST FAILED") + } + + jobFetchHelperForTest.start() + while(true) { + if (!jobFetchHelperForTest.isAlive) { + exception = jobFetchHelperForTest.getException() + break + } + } + assertSoftly { + exception shouldNotBe null + } + } + } + } + } +}) diff --git a/src/test/kotlin/org/zowe/explorer/dataops/JobFetchProviderTestSpec.kt b/src/test/kotlin/org/zowe/explorer/dataops/JobFetchProviderTestSpec.kt new file mode 100644 index 000000000..1be97ec39 --- /dev/null +++ b/src/test/kotlin/org/zowe/explorer/dataops/JobFetchProviderTestSpec.kt @@ -0,0 +1,307 @@ +/* + * This program and the accompanying materials are made available under the terms of the + * Eclipse Public License v2.0 which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-v20.html + * + * SPDX-License-Identifier: EPL-2.0 + * + * Copyright IBA Group 2020 + */ + +package org.zowe.explorer.dataops + +import com.intellij.openapi.application.ApplicationManager +import com.intellij.openapi.progress.ProgressIndicator +import com.intellij.openapi.vfs.VirtualFile +import com.intellij.testFramework.LightProjectDescriptor +import com.intellij.testFramework.fixtures.IdeaTestFixtureFactory +import com.intellij.testFramework.fixtures.impl.LightTempDirTestFixtureImpl +import org.zowe.explorer.api.ZosmfApi +import org.zowe.explorer.config.connect.ConnectionConfig +import org.zowe.explorer.config.connect.authToken +import org.zowe.explorer.config.ws.JobsFilter +import org.zowe.explorer.dataops.attributes.AttributesService +import org.zowe.explorer.dataops.attributes.FileAttributes +import org.zowe.explorer.dataops.attributes.JobsRequester +import org.zowe.explorer.dataops.attributes.RemoteJobAttributes +import org.zowe.explorer.dataops.attributes.RemoteJobAttributesService +import org.zowe.explorer.dataops.fetch.JobFetchProvider +import org.zowe.explorer.testutils.testServiceImpl.TestDataOpsManagerImpl +import org.zowe.explorer.testutils.testServiceImpl.TestZosmfApiImpl +import org.zowe.explorer.utils.cancelByIndicator +import org.zowe.explorer.utils.service +import org.zowe.explorer.vfs.MFVirtualFile +import io.kotest.assertions.assertSoftly +import io.kotest.assertions.throwables.shouldThrow +import io.kotest.core.spec.style.ShouldSpec +import io.kotest.matchers.collections.shouldHaveSize +import io.kotest.matchers.shouldBe +import io.kotest.matchers.throwable.shouldHaveMessage +import io.mockk.Runs +import io.mockk.clearAllMocks +import io.mockk.every +import io.mockk.just +import io.mockk.mockk +import io.mockk.spyk +import io.mockk.unmockkAll +import org.zowe.kotlinsdk.ExecData +import org.zowe.kotlinsdk.JESApi +import org.zowe.kotlinsdk.Job +import retrofit2.Call +import retrofit2.Response +import java.lang.reflect.InvocationTargetException + +class JobFetchProviderTestSpec : ShouldSpec({ + + beforeSpec { + // FIXTURE SETUP TO HAVE ACCESS TO APPLICATION INSTANCE + val factory = IdeaTestFixtureFactory.getFixtureFactory() + val projectDescriptor = LightProjectDescriptor.EMPTY_PROJECT_DESCRIPTOR + val fixtureBuilder = factory.createLightFixtureBuilder(projectDescriptor, "for-mainframe") + val fixture = fixtureBuilder.fixture + val myFixture = IdeaTestFixtureFactory.getFixtureFactory().createCodeInsightFixture( + fixture, + LightTempDirTestFixtureImpl(true) + ) + myFixture.setUp() + } + + afterSpec { + clearAllMocks() + } + + context("dataops module: fetch") { + + context("JobFetchProvider common") { + val mockedConnectionConfig = mockk() + every { mockedConnectionConfig.authToken } returns "auth_token" + every { mockedConnectionConfig.url } returns "test_url" + val mockedQuery = mockk>() + val mockedRequest = mockk() + every { mockedRequest.owner } returns "TESTOWNER" + every { mockedRequest.prefix } returns "TESTPREFIX" + every { mockedRequest.userCorrelatorFilter } returns "TESTFILTER" + val progressMockk = mockk() + every { mockedQuery.request } returns mockedRequest + every { mockedQuery.connectionConfig } returns mockedConnectionConfig + + val job1 = mockk() + val job2 = mockk() + every { job1.execStarted } returns null + every { job1.execEnded } returns null + every { job1.execSubmitted } returns null + every { job2.execStarted } returns null + every { job2.execEnded } returns null + every { job2.execSubmitted } returns null + every { job1.jobId } returns "TSUTEST1" + every { job2.jobId } returns "TSUTEST2" + every { job1.jobName } returns "TESTJOB1" + every { job2.jobName } returns "TESTJOB2" + + val jobs = mutableListOf(job1, job2) + val mockedCall = mockk>>() + val mockedResponse = mockk>>() + every { mockedCall.execute() } returns mockedResponse + every { mockedResponse.body() } returns jobs + + val zosmfApi = ApplicationManager.getApplication().service() as TestZosmfApiImpl + zosmfApi.testInstance = mockk() + val mockedApi = mockk() + every { zosmfApi.testInstance.getApi(JESApi::class.java, mockedConnectionConfig) } returns mockedApi + every { + mockedApi.getFilteredJobs( + basicCredentials = any() as String, + owner = any() as String, + prefix = any() as String, + userCorrelator = any() as String, + execData = any() as ExecData + ) + } returns mockedCall + every { + mockedApi.getFilteredJobs( + basicCredentials = any() as String, + owner = any() as String, + prefix = any() as String, + userCorrelator = any() as String, + execData = any() as ExecData + ).cancelByIndicator(progressMockk) + } returns mockedCall + every { + mockedApi.getFilteredJobs( + basicCredentials = any() as String, + jobId = any() as String, + execData = any() as ExecData + ) + } returns mockedCall + every { + mockedApi.getFilteredJobs( + basicCredentials = any() as String, + jobId = any() as String, + execData = any() as ExecData + ).cancelByIndicator(progressMockk) + } returns mockedCall + + val dataOpsManagerService = + ApplicationManager.getApplication().service() as TestDataOpsManagerImpl + val componentManager = dataOpsManagerService.componentManager + + // needed for cleanupUnusedFile test + val mockedVirtualFile = mockk() + val mockedFileAttributes = mockk() + val mockedAttributesService = mockk() + val mockedJobsRequester = mockk() + val requesters = mutableListOf(mockedJobsRequester) + + afterEach { unmockkAll() } + + val jobFetchProviderForTest = spyk(JobFetchProvider(dataOpsManagerService), recordPrivateCalls = true) + + should("fetchResponse get job attributes if job ID is not null and response is successful and exec dates/times are null") { + + val fetchResponseMethodRef = + jobFetchProviderForTest::class.java.declaredMethods.first { it.name == "fetchResponse" } + fetchResponseMethodRef.trySetAccessible() + every { mockedRequest.jobId } returns "TSUTEST" + every { mockedResponse.isSuccessful } returns true + + val jobAttributes = + (fetchResponseMethodRef.invoke(jobFetchProviderForTest, mockedQuery, progressMockk) as Collection<*>) + + assertSoftly { + jobAttributes shouldHaveSize jobs.size + } + } + + should("fetchResponse get job attributes if job ID is null and response is successful") { + + val fetchResponseMethodRef = + jobFetchProviderForTest::class.java.declaredMethods.first { it.name == "fetchResponse" } + fetchResponseMethodRef.trySetAccessible() + every { mockedRequest.jobId } returns "" + every { mockedResponse.isSuccessful } returns true + + val jobAttributes = + (fetchResponseMethodRef.invoke(jobFetchProviderForTest, mockedQuery, progressMockk) as Collection<*>) + + assertSoftly { + jobAttributes shouldHaveSize jobs.size + } + } + + should("fetchResponse get job attributes if exec dates/times are not null") { + + val fetchResponseMethodRef = + jobFetchProviderForTest::class.java.declaredMethods.first { it.name == "fetchResponse" } + fetchResponseMethodRef.trySetAccessible() + every { mockedRequest.jobId } returns "TSUTEST" + every { mockedResponse.isSuccessful } returns true + + every { job1.execStarted } returns "" + every { job1.execEnded } returns "" + every { job1.execSubmitted } returns "" + every { job2.execStarted } returns "" + every { job2.execEnded } returns "" + every { job2.execSubmitted } returns "" + + val jobAttributes = + (fetchResponseMethodRef.invoke(jobFetchProviderForTest, mockedQuery, progressMockk) as Collection<*>) + + assertSoftly { + jobAttributes shouldHaveSize jobs.size + } + } + + should("fetchResponse get job attributes if response does not return any job") { + + val fetchResponseMethodRef = + jobFetchProviderForTest::class.java.declaredMethods.first { it.name == "fetchResponse" } + fetchResponseMethodRef.trySetAccessible() + every { mockedRequest.jobId } returns "TSUTEST" + every { mockedResponse.isSuccessful } returns true + every { mockedResponse.body() } returns emptyList() + + val jobAttributes = + (fetchResponseMethodRef.invoke(jobFetchProviderForTest, mockedQuery, progressMockk) as Collection<*>) + + assertSoftly { + jobAttributes shouldHaveSize 0 + } + } + + should("fetchResponse get job attributes if response was not successful") { + val fetchResponseMethodRef = + jobFetchProviderForTest::class.java.declaredMethods.first { it.name == "fetchResponse" } + fetchResponseMethodRef.trySetAccessible() + every { mockedRequest.jobId } returns "TSUTEST" + every { mockedResponse.isSuccessful } returns false + every { mockedResponse.code() } returns 404 + every { mockedResponse.body() } returns emptyList() + + val exception = shouldThrow { + fetchResponseMethodRef.invoke(jobFetchProviderForTest, mockedQuery, progressMockk) + } + + assertSoftly { + exception.cause!! shouldHaveMessage "Cannot retrieve Job files list\nCode: 404" + } + } + + should("cleanup unused file if connection config of the query is the same as for job file") { + var cleanupPerformed = false + val cleanupUnusedFileMethodRef = + jobFetchProviderForTest::class.java.declaredMethods.first { it.name == "cleanupUnusedFile" } + cleanupUnusedFileMethodRef.trySetAccessible() + dataOpsManagerService.testInstance = object : TestDataOpsManagerImpl(componentManager) { + override fun getAttributesService( + attributesClass: Class, + vFileClass: Class + ): AttributesService { + return mockedAttributesService as AttributesService + } + } + + every { mockedAttributesService.getAttributes(mockedVirtualFile) } returns mockedFileAttributes + every { mockedAttributesService.clearAttributes(mockedVirtualFile) } just Runs + + every { mockedJobsRequester.connectionConfig } returns mockedConnectionConfig + every { mockedFileAttributes.requesters } returns requesters + + every { mockedVirtualFile.delete(any() as JobFetchProvider) } answers { + cleanupPerformed = true + } + + cleanupUnusedFileMethodRef.invoke(jobFetchProviderForTest, mockedVirtualFile, mockedQuery) + + assertSoftly { + cleanupPerformed shouldBe true + } + } + + should("cleanup file if connections are not the same") { + var cleanupPerformed = false + val cleanupUnusedFileMethodRef = + jobFetchProviderForTest::class.java.declaredMethods.first { it.name == "cleanupUnusedFile" } + cleanupUnusedFileMethodRef.trySetAccessible() + + every { mockedJobsRequester.connectionConfig } returns mockk() + every { mockedFileAttributes.requesters } returns requesters + every { + mockedAttributesService.updateAttributes( + mockedVirtualFile, + any() as RemoteJobAttributes.() -> Unit + ) + } answers { + cleanupPerformed = true + secondArg Unit>().invoke(mockedFileAttributes) + } + + cleanupUnusedFileMethodRef.invoke(jobFetchProviderForTest, mockedVirtualFile, mockedQuery) + + assertSoftly { + cleanupPerformed shouldBe true + } + } + + } + } +}) diff --git a/src/test/kotlin/org/zowe/explorer/dataops/OperationsTestSpec.kt b/src/test/kotlin/org/zowe/explorer/dataops/OperationsTestSpec.kt index 2d4d16ad6..b102100d5 100644 --- a/src/test/kotlin/org/zowe/explorer/dataops/OperationsTestSpec.kt +++ b/src/test/kotlin/org/zowe/explorer/dataops/OperationsTestSpec.kt @@ -29,7 +29,10 @@ import org.zowe.explorer.dataops.attributes.Requester import org.zowe.explorer.dataops.attributes.UssRequester import org.zowe.explorer.dataops.content.synchronizer.ContentSynchronizer import org.zowe.explorer.dataops.content.synchronizer.DocumentedSyncProvider +import org.zowe.explorer.dataops.exceptions.CallException import org.zowe.explorer.dataops.operations.DeleteOperation +import org.zowe.explorer.dataops.operations.ZOSInfoOperation +import org.zowe.explorer.dataops.operations.ZOSInfoOperationRunner import org.zowe.explorer.dataops.operations.mover.CrossSystemMemberOrUssFileOrSequentialToUssDirMover import org.zowe.explorer.dataops.operations.mover.MoveCopyOperation import org.zowe.explorer.dataops.operations.mover.RemoteToLocalFileMover @@ -50,6 +53,8 @@ import io.mockk.spyk import io.mockk.unmockkAll import org.zowe.kotlinsdk.DataAPI import org.zowe.kotlinsdk.FilePath +import org.zowe.kotlinsdk.InfoAPI +import org.zowe.kotlinsdk.InfoResponse import org.zowe.kotlinsdk.XIBMDataType import org.zowe.kotlinsdk.annotations.ZVersion import retrofit2.Call @@ -57,6 +62,7 @@ import retrofit2.Response import java.io.File import java.nio.charset.Charset import java.nio.charset.StandardCharsets +import kotlin.reflect.KFunction inline fun mockkRequesters( attributes: MFRemoteFileAttributes<*, *>, @@ -94,8 +100,72 @@ class OperationsTestSpec : WithApplicationShouldSpec({ clearAllMocks() } context("dataops module: operations/ZOSIntoOperationRunner") { - context("run") { - should("perform Info operation") {} + val zosInfoOperationRunner = ZOSInfoOperationRunner() + + val zosmfApi = ApplicationManager.getApplication().service() as TestZosmfApiImpl + zosmfApi.testInstance = mockk() + + val progressIndicatorMockk = mockk() + val responseMockk = mockk>() + every { + zosmfApi.testInstance.getApi(InfoAPI::class.java, any()) + .getSystemInfo() + .cancelByIndicator(progressIndicatorMockk) + .execute() + } returns responseMockk + + val operationMockk = mockk() + every { operationMockk.connectionConfig } returns mockk() + + val callExceptionRef: (Response<*>, String) -> CallException = ::CallException + beforeEach { + mockkStatic(callExceptionRef as KFunction<*>) + } + afterEach { + unmockkAll() + } + + // ZOSIntoOperationRunner.run + should("perform Info operation with successful response") { + val expected = InfoResponse(zosVersion = "2.3", zosmfHostname = "host", zosmfPort = "port") + + every { responseMockk.isSuccessful } returns true + every { responseMockk.body() } returns expected + + val actual = zosInfoOperationRunner.run(operationMockk, progressIndicatorMockk) + + assertSoftly { actual shouldBe expected } + } + should("perform Info operation with unsuccessful response") { + val expected = CallException(500, "An internal error has occurred") + + every { responseMockk.isSuccessful } returns false + every { CallException(responseMockk, any()) } returns expected + + var actual: Throwable? = null + runCatching { + zosInfoOperationRunner.run(operationMockk, progressIndicatorMockk) + }.onFailure { + actual = it + } + + assertSoftly { actual shouldBe expected } + } + should("perform Info operation when response body is null") { + val expected = CallException(500, "Cannot parse z/OSMF info request body") + + every { responseMockk.isSuccessful } returns true + every { responseMockk.body() } returns null + every { CallException(responseMockk, any()) } returns expected + + var actual: Throwable? = null + runCatching { + zosInfoOperationRunner.run(operationMockk, progressIndicatorMockk) + }.onFailure { + actual = it + } + + assertSoftly { actual shouldBe expected } } } context("dataops module: operations/RenameOperationRunner") { diff --git a/src/test/kotlin/org/zowe/explorer/dataops/RemoteFileFetchProviderBaseTestSpec.kt b/src/test/kotlin/org/zowe/explorer/dataops/RemoteFileFetchProviderBaseTestSpec.kt new file mode 100644 index 000000000..b7bfc8261 --- /dev/null +++ b/src/test/kotlin/org/zowe/explorer/dataops/RemoteFileFetchProviderBaseTestSpec.kt @@ -0,0 +1,126 @@ +/* + * This program and the accompanying materials are made available under the terms of the + * Eclipse Public License v2.0 which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-v20.html + * + * SPDX-License-Identifier: EPL-2.0 + * + * Copyright IBA Group 2020 + */ + +package org.zowe.explorer.dataops + +import com.intellij.ide.util.treeView.AbstractTreeNode +import com.intellij.openapi.application.ApplicationManager +import com.intellij.testFramework.LightProjectDescriptor +import com.intellij.testFramework.fixtures.IdeaTestFixtureFactory +import com.intellij.testFramework.fixtures.impl.LightTempDirTestFixtureImpl +import org.zowe.explorer.config.connect.ConnectionConfig +import org.zowe.explorer.config.ws.DSMask +import org.zowe.explorer.dataops.fetch.DatasetFileFetchProvider +import org.zowe.explorer.testutils.WithApplicationShouldSpec +import org.zowe.explorer.testutils.testServiceImpl.TestDataOpsManagerImpl +import org.zowe.explorer.utils.service +import io.kotest.assertions.assertSoftly +import io.kotest.core.spec.style.ShouldSpec +import io.kotest.matchers.maps.shouldContainExactly +import io.kotest.matchers.maps.shouldHaveSize +import io.kotest.matchers.shouldBe +import io.mockk.clearAllMocks +import io.mockk.mockk +import io.mockk.unmockkAll +import io.mockk.verify +import org.junit.platform.commons.util.ReflectionUtils +import java.lang.reflect.Field +import java.time.LocalDateTime +import java.util.concurrent.locks.ReentrantLock + +class RemoteFileFetchProviderBaseTestSpec: WithApplicationShouldSpec({ + + afterSpec { + clearAllMocks() + } + + context("refresh cache test spec") { + + val dataOpsManagerService = + ApplicationManager.getApplication().service() as TestDataOpsManagerImpl + val classUnderTest = DatasetFileFetchProvider(dataOpsManagerService) + + val queryMock = mockk>() + val nodeMock = mockk>() + val queryOtherMock = mockk>() + val nodeOtherMock = mockk>() + val lastRefreshDate = LocalDateTime.now() + val lastRefreshDateOther = LocalDateTime.of(2023, 12, 30, 10, 0, 0) + + val refreshCacheStateField = ReflectionUtils + .findFields( + DatasetFileFetchProvider::class.java, { f -> f.name.equals("refreshCacheState") }, + ReflectionUtils.HierarchyTraversalMode.TOP_DOWN + )[0] + refreshCacheStateField.isAccessible = true + + context("applyRefreshCacheDate") { + should("should add new entry with given node and query and last refreshDate") { + //given + val actualRefreshCacheMap = mutableMapOf, RemoteQuery>, LocalDateTime>() + val expectedRefreshCacheMap = mutableMapOf(Pair(Pair(nodeMock,queryMock), lastRefreshDate)) + refreshCacheStateField.set(classUnderTest, actualRefreshCacheMap) + + //when + classUnderTest.applyRefreshCacheDate(queryMock, nodeMock, lastRefreshDate) + + //then + assertSoftly { + actualRefreshCacheMap shouldContainExactly expectedRefreshCacheMap + } + } + + should("should not add new entry if entry already present") { + //given + val actualRefreshCacheMap = mutableMapOf(Pair(Pair(nodeMock,queryMock), lastRefreshDate)) + val expectedRefreshCacheMap = mutableMapOf(Pair(Pair(nodeMock,queryMock), lastRefreshDate)) + refreshCacheStateField.set(classUnderTest, actualRefreshCacheMap) + + //when + classUnderTest.applyRefreshCacheDate(queryMock, nodeMock, lastRefreshDate) + + //then + assertSoftly { + actualRefreshCacheMap shouldContainExactly expectedRefreshCacheMap + } + } + } + context("findCacheRefreshDateIfPresent") { + should("should find the last refreshDate for the node for the given query") { + //given + val refreshCacheMapForTest = mutableMapOf(Pair(Pair(nodeMock,queryMock), lastRefreshDate), Pair(Pair(nodeOtherMock,queryOtherMock), lastRefreshDateOther)) + refreshCacheStateField.set(classUnderTest, refreshCacheMapForTest) + + //when + val actualRefreshDate = classUnderTest.findCacheRefreshDateIfPresent(queryMock) + + //then + assertSoftly { + actualRefreshDate shouldBe lastRefreshDate + } + } + should("should not find the last refreshDate and return null for the node for the given query") { + //given + val refreshCacheMapForTest = mutableMapOf(Pair(Pair(nodeMock,queryMock), lastRefreshDate), Pair(Pair(nodeOtherMock,queryOtherMock), lastRefreshDateOther)) + refreshCacheStateField.set(classUnderTest, refreshCacheMapForTest) + val queryMockForTest = mockk>() + + //when + val actualRefreshDate = classUnderTest.findCacheRefreshDateIfPresent(queryMockForTest) + + //then + assertSoftly { + actualRefreshDate shouldBe null + } + } + } + unmockkAll() + } +}) diff --git a/src/test/kotlin/org/zowe/explorer/explorer/actions/PurgeJobActionTestSpec.kt b/src/test/kotlin/org/zowe/explorer/explorer/actions/PurgeJobActionTestSpec.kt index 7c28f640d..3a526565b 100644 --- a/src/test/kotlin/org/zowe/explorer/explorer/actions/PurgeJobActionTestSpec.kt +++ b/src/test/kotlin/org/zowe/explorer/explorer/actions/PurgeJobActionTestSpec.kt @@ -1,3 +1,13 @@ +/* + * This program and the accompanying materials are made available under the terms of the + * Eclipse Public License v2.0 which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-v20.html + * + * SPDX-License-Identifier: EPL-2.0 + * + * Copyright IBA Group 2020 + */ + package org.zowe.explorer.explorer.actions import com.intellij.notification.NotificationType @@ -10,6 +20,7 @@ import com.intellij.openapi.vfs.VirtualFile import org.zowe.explorer.api.ZosmfApi import org.zowe.explorer.config.connect.ConnectionConfig import org.zowe.explorer.config.connect.CredentialService +import org.zowe.explorer.config.connect.authToken import org.zowe.explorer.config.ws.JobsFilter import org.zowe.explorer.dataops.DataOpsManager import org.zowe.explorer.dataops.Operation @@ -19,31 +30,39 @@ import org.zowe.explorer.dataops.attributes.JobsRequester import org.zowe.explorer.dataops.attributes.RemoteJobAttributes import org.zowe.explorer.dataops.log.JobLogFetcher import org.zowe.explorer.dataops.log.MFLogger +import org.zowe.explorer.dataops.operations.jobs.PurgeJobOperation import org.zowe.explorer.explorer.Explorer import org.zowe.explorer.explorer.JesWorkingSetImpl -import org.zowe.explorer.explorer.ui.ErrorNode +import org.zowe.explorer.explorer.ui.EXPLORER_VIEW import org.zowe.explorer.explorer.ui.JesExplorerView import org.zowe.explorer.explorer.ui.JesFilterNode +import org.zowe.explorer.explorer.ui.JesWsNode import org.zowe.explorer.explorer.ui.JobNode import org.zowe.explorer.explorer.ui.NodeData import org.zowe.explorer.explorer.ui.getExplorerView import org.zowe.explorer.testutils.WithApplicationShouldSpec import org.zowe.explorer.testutils.testServiceImpl.TestDataOpsManagerImpl +import org.zowe.explorer.testutils.testServiceImpl.TestZosmfApiImpl import org.zowe.explorer.ui.build.jobs.JobBuildTreeView -import org.zowe.explorer.utils.gson import org.zowe.explorer.utils.service import org.zowe.explorer.vfs.MFVirtualFile import io.kotest.assertions.assertSoftly import io.kotest.matchers.shouldBe +import io.mockk.Runs import io.mockk.clearAllMocks import io.mockk.every +import io.mockk.just import io.mockk.justRun import io.mockk.mockk import io.mockk.mockkObject import io.mockk.spyk +import io.mockk.unmockkAll +import io.mockk.unmockkObject +import org.junit.jupiter.api.Assertions.assertThrows import org.zowe.kotlinsdk.CancelJobPurgeOutRequest import org.zowe.kotlinsdk.JESApi import org.zowe.kotlinsdk.Job +import org.zowe.kotlinsdk.gson import retrofit2.Call import retrofit2.Response @@ -56,8 +75,8 @@ class PurgeJobActionTestSpec : WithApplicationShouldSpec({ val purgeAction = PurgeJobAction() val mockActionEventForJesEx = mockk() val jesExplorerView = mockk() - val job = mockk() + every { job.jobName } returns "name" every { job.jobId } returns "id" val connectionConfig = mockk() @@ -181,13 +200,6 @@ class PurgeJobActionTestSpec : WithApplicationShouldSpec({ } - var isOperationSucceededForJesEx = false - every { - explorer.showNotification(any(), any(), NotificationType.INFORMATION, any()) - } answers { - isOperationSucceededForJesEx = true - } - var isOperationSucceededForJobsLog = false every { jobsLogView.showNotification(any(), any(), any(), NotificationType.INFORMATION) @@ -195,22 +207,15 @@ class PurgeJobActionTestSpec : WithApplicationShouldSpec({ isOperationSucceededForJobsLog = true } - var isVisibleInJes = false - every { mockActionEventForJesEx.presentation.isVisible = true } answers { isVisibleInJes = true } - every { job.status } returns mockk() var isEnabledInJobsLog = true every { mockActionEventForJobsLog.presentation.isEnabled = false } answers { isEnabledInJobsLog = false } - purgeAction.actionPerformed(mockActionEventForJesEx) - purgeAction.update(mockActionEventForJesEx) purgeAction.actionPerformed(mockActionEventForJobsLog) purgeAction.update(mockActionEventForJobsLog) assertSoftly { - isOperationSucceededForJesEx shouldBe true isOperationSucceededForJobsLog shouldBe true - isVisibleInJes shouldBe true isEnabledInJobsLog shouldBe true purgeAction.isDumbAware shouldBe true } @@ -218,23 +223,6 @@ class PurgeJobActionTestSpec : WithApplicationShouldSpec({ } should("perform purge on job with error") { - val updateJesAction = mockk() - val jesViewForUpdate = mockk() - every { updateJesAction.getExplorerView() } returns jesViewForUpdate - every { jesViewForUpdate.mySelectedNodesData } returns listOf() - - val updateJesAction2 = mockk() - val jesViewForUpdate2 = mockk() - every { updateJesAction2.getExplorerView() } returns jesViewForUpdate2 - val errorNode = mockk>() - every { jesViewForUpdate2.mySelectedNodesData } returns listOf( - NodeData( - errorNode, - virtualFile, - null - ) - ) - dataOpsManager = ApplicationManager.getApplication().service() as TestDataOpsManagerImpl dataOpsManager.testInstance = object : TestDataOpsManagerImpl(explorer.componentManager) { override fun tryToGetAttributes(file: VirtualFile): FileAttributes { @@ -246,13 +234,6 @@ class PurgeJobActionTestSpec : WithApplicationShouldSpec({ } } - var isOperationFailedForJesEx = false - every { - explorer.showNotification(any(), any(), NotificationType.ERROR, any()) - } answers { - isOperationFailedForJesEx = true - } - var isOperationFailedForJobsLog = false every { jobsLogView.showNotification(any(), any(), any(), NotificationType.ERROR) @@ -271,28 +252,265 @@ class PurgeJobActionTestSpec : WithApplicationShouldSpec({ var isEnabledInJobsLog = true every { mockActionEventForJobsLog.presentation.isEnabled = false } answers { isEnabledInJobsLog = false } - var isVisibleForJes = true - every { updateJesAction.presentation.isVisible = false } answers { isVisibleForJes = false } - var isVisibleForJes2 = true - every { updateJesAction2.presentation.isVisible = false } answers { isVisibleForJes2 = false } - - purgeAction.actionPerformed(mockActionEventForJesEx) purgeAction.actionPerformed(mockActionEventForJobsLog) purgeAction.actionPerformed(mockActionEventWithoutDataContext) purgeAction.update(mockActionEventWithoutDataContext) purgeAction.update(mockActionEventForJobsLog) - purgeAction.update(updateJesAction) - purgeAction.update(updateJesAction2) assertSoftly { - isOperationFailedForJesEx shouldBe true isOperationFailedForJobsLog shouldBe true isEnabledInJobsLog shouldBe false - isVisibleForJes shouldBe false - isVisibleForJes2 shouldBe false - isOperationFailedForNoContextAction shouldBe true } } + unmockkAll() + } + context("api spec") { + + val zosmfApi = ApplicationManager.getApplication().service() as TestZosmfApiImpl + zosmfApi.testInstance = mockk() + + val responseMockk = mockk>>() + every { + zosmfApi.testInstance.getApi(JESApi::class.java, any()) + .getFilteredJobs(any(), any(), any(), any(), any(), any(), any(), any()) + .execute() + } returns responseMockk + + val purgeAction = spyk(PurgeJobAction()) + val projectMock = mockk() + val mockActionEventForJesEx = mockk() + every { mockActionEventForJesEx.project } returns projectMock + every { mockActionEventForJesEx.presentation.text = any() } just Runs + val jesExplorerView = mockk() + val explorerMock = mockk>() + every { mockActionEventForJesEx.getData(EXPLORER_VIEW) } returns jesExplorerView + every { jesExplorerView.explorer } returns explorerMock + val connectionConfig = mockk() + every { connectionConfig.uuid } returns "uuid" + every { connectionConfig.authToken } returns "auth_token" + val diffConnectionConfig = mockk() + every { diffConnectionConfig.uuid } returns "diffUuid" + + context("common test spec") { + + val jobParent1 = mockk() + val jobParentParent1 = mockk() + every { jobParent1.parent } returns jobParentParent1 + every { jobParent1.query } returns mockk() + every { jobParent1.query?.connectionConfig } returns connectionConfig + every { jobParent1.query?.request } returns mockk() + every { jobParent1.query?.request?.prefix } returns "prefix_1" + every { jobParent1.query?.request?.owner } returns "owner" + every { jobParent1.query?.request?.userCorrelatorFilter } returns "filter_1" + + every { jobParentParent1.unit } returns mockk() + every { jobParentParent1.unit.name } returns "firstWS" + + val jobParent2 = mockk() + val jobParentParent2 = mockk() + every { jobParent2.parent } returns jobParentParent2 + every { jobParent2.query } returns mockk() + every { jobParent2.query?.connectionConfig } returns connectionConfig + every { jobParent2.query?.request } returns mockk() + every { jobParent2.query?.request?.prefix } returns "prefix_2" + every { jobParent2.query?.request?.owner } returns "owner" + every { jobParent2.query?.request?.userCorrelatorFilter } returns "filter_2" + every { jobParentParent2.unit } returns mockk() + every { jobParentParent2.unit.name } returns "secondWS" + + context("isSelectedJobNodesFromSameWS") { + + val jesWorkingSet1 = mockk() + every { jesWorkingSet1.connectionConfig } returns connectionConfig + val jesWorkingSet2 = mockk() + every { jesWorkingSet2.connectionConfig } returns connectionConfig + val jesWorkingSet3 = mockk() + every { jesWorkingSet3.connectionConfig } returns diffConnectionConfig + + val jobNode1 = mockk() + every { jobNode1.unit } returns jesWorkingSet1 + every { jobNode1.query } returns mockk() + every { jobNode1.query?.connectionConfig } returns connectionConfig + every { jobNode1.parent } returns jobParent1 + val jobNode2 = mockk() + every { jobNode2.unit } returns jesWorkingSet2 + every { jobNode2.query } returns mockk() + every { jobNode2.query?.connectionConfig } returns connectionConfig + every { jobNode2.parent } returns jobParent2 + val jobNode3 = mockk() + every { jobNode3.unit } returns jesWorkingSet3 + every { jobNode3.query } returns mockk() + every { jobNode3.query?.connectionConfig } returns diffConnectionConfig + val wrongJobNode4 = mockk() + every { wrongJobNode4.unit } returns jesWorkingSet3 + + val virtualFileMock = mockk() + val jobInfo1 = mockk() + val jobInfo2 = mockk() + every { jobInfo1.jobId } returns "TSU01" + every { jobInfo2.jobId } returns "TSU02" + every { jobInfo1.jobName } returns "test1" + every { jobInfo2.jobName } returns "test2" + val requester1 = mockk() + val requester2 = mockk() + every { requester1.connectionConfig } returns connectionConfig + every { requester2.connectionConfig } returns connectionConfig + + val attributes1 = mockk() + every { attributes1.jobInfo } returns jobInfo1 + every { attributes1.requesters[0] } returns requester1 + val attributes2 = mockk() + every { attributes2.jobInfo } returns jobInfo2 + every { attributes2.requesters[0] } returns requester2 + val attributes3 = mockk() + val attributes4 = mockk() + + var mySelectedData: List> + var isEnableAndVisibleAction: Boolean + + every { mockActionEventForJesEx.presentation.isEnabledAndVisible = false } answers { + isEnableAndVisibleAction = false + } + every { mockActionEventForJesEx.presentation.isEnabledAndVisible = true } answers { + isEnableAndVisibleAction = true + } + + should("action is not visible when selected job nodes contains wrong node") { + isEnableAndVisibleAction = true + val nodeData1 = NodeData(jobNode1, virtualFileMock, attributes1) + mockkObject(nodeData1) + val nodeData2 = NodeData(jobNode2, virtualFileMock, attributes2) + mockkObject(nodeData2) + val nodeData3 = NodeData(jobNode3, virtualFileMock, attributes3) + mockkObject(nodeData3) + val nodeData4 = NodeData(wrongJobNode4, virtualFileMock, attributes4) + mockkObject(nodeData4) + mySelectedData = listOf(nodeData1, nodeData2, nodeData3, nodeData4) + every { jesExplorerView.mySelectedNodesData } returns mySelectedData + every { mockActionEventForJesEx.presentation.isEnabledAndVisible } returns false + purgeAction.update(mockActionEventForJesEx) + + assertSoftly { + isEnableAndVisibleAction shouldBe false + } + unmockkObject(nodeData1, nodeData2, nodeData3, nodeData4) + } + should("action is not visible when selected job nodes from different connections") { + isEnableAndVisibleAction = true + val nodeData1 = NodeData(jobNode1, virtualFileMock, attributes1) + mockkObject(nodeData1) + val nodeData2 = NodeData(jobNode2, virtualFileMock, attributes2) + mockkObject(nodeData2) + val nodeData3 = NodeData(jobNode3, virtualFileMock, attributes3) + mockkObject(nodeData3) + mySelectedData = listOf(nodeData1, nodeData2, nodeData3) + every { jesExplorerView.mySelectedNodesData } returns mySelectedData + purgeAction.update(mockActionEventForJesEx) + + assertSoftly { + isEnableAndVisibleAction shouldBe false + } + unmockkObject(nodeData1, nodeData2, nodeData3) + } + should("action is not visible when selected job nodes from different working sets") { + isEnableAndVisibleAction = true + val nodeData1 = NodeData(jobNode1, virtualFileMock, attributes1) + mockkObject(nodeData1) + val nodeData2 = NodeData(jobNode2, virtualFileMock, attributes2) + mockkObject(nodeData2) + mySelectedData = listOf(nodeData1, nodeData2) + every { jesExplorerView.mySelectedNodesData } returns mySelectedData + purgeAction.update(mockActionEventForJesEx) + + assertSoftly { + isEnableAndVisibleAction shouldBe false + } + unmockkObject(nodeData1, nodeData2) + } + should("action is visible when selected job nodes from the same connections and same working set") { + isEnableAndVisibleAction = false + every { jobParentParent1.unit.name } returns "sameWS" + every { jobParentParent2.unit.name } returns "sameWS" + val nodeData1 = NodeData(jobNode1, virtualFileMock, attributes1) + mockkObject(nodeData1) + val nodeData2 = NodeData(jobNode2, virtualFileMock, attributes2) + mockkObject(nodeData2) + mySelectedData = listOf(nodeData1, nodeData2) + every { jesExplorerView.mySelectedNodesData } returns mySelectedData + purgeAction.update(mockActionEventForJesEx) + + assertSoftly { + isEnableAndVisibleAction shouldBe true + } + unmockkObject(nodeData1, nodeData2) + } + should("purge actionPerformed when jobs haven't been purged due to error") { + var isJobsPurged = true + val nodeData1 = NodeData(jobNode1, virtualFileMock, attributes1) + mockkObject(nodeData1) + val nodeData2 = NodeData(jobNode2, virtualFileMock, attributes2) + mockkObject(nodeData2) + mySelectedData = listOf(nodeData1, nodeData2) + every { jesExplorerView.mySelectedNodesData } returns mySelectedData + every { + explorerMock.showNotification( + any() as String, + any() as String, + any() as NotificationType, + any() as Project + ) + } just Runs + every { responseMockk.isSuccessful } returns true + every { responseMockk.body() } returns listOf(jobInfo1, jobInfo2) + every { jobParent1.cleanCache() } answers { + isJobsPurged = false + } + every { jobParent2.cleanCache() } answers { + isJobsPurged = false + } + purgeAction.actionPerformed(mockActionEventForJesEx) + + assertSoftly { + isJobsPurged shouldBe false + } + } + should("purge actionPerformed when jobs have been purged successfully") { + var isJobsPurged = false + val dataOpsManager = ApplicationManager.getApplication().service() as TestDataOpsManagerImpl + dataOpsManager.testInstance = mockk() + every { + dataOpsManager.testInstance.performOperation( + any() as PurgeJobOperation, + any() as ProgressIndicator + ) + } answers { + isJobsPurged = true + CancelJobPurgeOutRequest() + } + every { responseMockk.isSuccessful } returns true + every { responseMockk.body() } returns listOf(jobInfo1, jobInfo2) + + purgeAction.actionPerformed(mockActionEventForJesEx) + + assertSoftly { + isJobsPurged shouldBe true + } + } + should("purge actionPerformed when jobs have been purged successfully, but refresh by filter fails") { + every { responseMockk.isSuccessful } returns false + every { responseMockk.body() } returns listOf() + every { jobParent1.unit } returns mockk() + every { jobParent1.unit.name } returns "firstFilter" + every { jobParent2.unit } returns mockk() + every { jobParent2.unit.name } returns "secondFilter" + + assertThrows(RuntimeException::class.java) { + purgeAction.actionPerformed(mockActionEventForJesEx) + } + } + } + } + unmockkAll() } } }) diff --git a/src/test/kotlin/org/zowe/explorer/explorer/ui/DSMaskNodeTestSpec.kt b/src/test/kotlin/org/zowe/explorer/explorer/ui/DSMaskNodeTestSpec.kt new file mode 100644 index 000000000..76dc4cffe --- /dev/null +++ b/src/test/kotlin/org/zowe/explorer/explorer/ui/DSMaskNodeTestSpec.kt @@ -0,0 +1,205 @@ +/* + * This program and the accompanying materials are made available under the terms of the + * Eclipse Public License v2.0 which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-v20.html + * + * SPDX-License-Identifier: EPL-2.0 + * + * Copyright IBA Group 2020 + */ + +package org.zowe.explorer.explorer.ui + +import com.intellij.ide.util.treeView.AbstractTreeNode +import com.intellij.openapi.application.ApplicationManager +import com.intellij.openapi.project.Project +import com.intellij.openapi.vfs.VirtualFile +import org.zowe.explorer.config.ws.DSMask +import org.zowe.explorer.dataops.DataOpsManager +import org.zowe.explorer.dataops.attributes.FileAttributes +import org.zowe.explorer.dataops.attributes.RemoteDatasetAttributes +import org.zowe.explorer.dataops.sort.SortQueryKeys +import org.zowe.explorer.explorer.FileExplorer +import org.zowe.explorer.explorer.FilesWorkingSetImpl +import org.zowe.explorer.testutils.WithApplicationShouldSpec +import org.zowe.explorer.testutils.testServiceImpl.TestDataOpsManagerImpl +import org.zowe.explorer.utils.service +import org.zowe.explorer.vfs.MFVirtualFile +import io.kotest.assertions.assertSoftly +import io.kotest.matchers.shouldBe +import io.mockk.* +import org.zowe.kotlinsdk.Dataset + +class DSMaskNodeTestSpec : WithApplicationShouldSpec({ + + afterSpec { + clearAllMocks() + unmockkAll() + } + + context("explorer module: ui/DSMaskNode") { + val mockedDSMask = mockk() + val mockedProject = mockk() + val mockedExplorerTreeNodeParent = mockk() + val mockedWorkingSet = mockk() + val mockedExplorer = mockk() + val mockedExplorerTreeStructure = mockk() + + every { mockedWorkingSet.explorer } returns mockedExplorer + every { mockedExplorerTreeStructure.registerNode(any()) } just Runs + + val classUnderTest = spyk(DSMaskNode(mockedDSMask, mockedProject, mockedExplorerTreeNodeParent, mockedWorkingSet, mockedExplorerTreeStructure)) + + context("sort children nodes") { + val unexpectedNode = mockk() + val mockedVFileChild1 = mockk() + val mockedVFileChild2 = mockk() + val mockedVFileChild3 = mockk() + val mockedAttributes1 = mockk() + val mockedAttributes2 = mockk() + val mockedAttributes3 = mockk() + val datasetInfo1 = mockk() + val datasetInfo2 = mockk() + val datasetInfo3 = mockk() + + val nodeToAttributesMap = mutableMapOf(Pair(mockedVFileChild1, mockedAttributes1), Pair(mockedVFileChild2, mockedAttributes2), Pair(mockedVFileChild3, mockedAttributes3)) + + val dataOpsManagerService = ApplicationManager.getApplication().service() as TestDataOpsManagerImpl + dataOpsManagerService.testInstance = object : TestDataOpsManagerImpl(ApplicationManager.getApplication()) { + override fun tryToGetAttributes(file: VirtualFile): FileAttributes? { + return nodeToAttributesMap[file] + } + } + + beforeEach { + every { mockedAttributes1.datasetInfo } returns datasetInfo1 + every { mockedAttributes2.datasetInfo } returns datasetInfo2 + every { mockedAttributes3.datasetInfo } returns datasetInfo3 + every { mockedAttributes1.datasetInfo.name } returns "AAAA" + every { mockedAttributes2.datasetInfo.name } returns "BBBB" + every { mockedAttributes3.datasetInfo.name } returns "CCCC" + every { mockedAttributes1.datasetInfo.lastReferenceDate } returns "2024/01/10" + every { mockedAttributes2.datasetInfo.lastReferenceDate } returns "2024/01/09" + every { mockedAttributes3.datasetInfo.lastReferenceDate } returns "2024/02/02" + } + + val mockedDataset1 = spyk( + FileLikeDatasetNode(mockedVFileChild1, mockedProject, mockedExplorerTreeNodeParent, mockedWorkingSet, mockedExplorerTreeStructure) + ) + val mockedDataset2 = spyk( + FileLikeDatasetNode(mockedVFileChild2, mockedProject, mockedExplorerTreeNodeParent, mockedWorkingSet, mockedExplorerTreeStructure) + ) + val mockedDataset3 = spyk( + LibraryNode(mockedVFileChild3, mockedProject, mockedExplorerTreeNodeParent, mockedWorkingSet, mockedExplorerTreeStructure) + ) + + every { mockedDataset1.virtualFile } returns mockedVFileChild1 + every { mockedDataset2.virtualFile } returns mockedVFileChild2 + every { mockedDataset3.virtualFile } returns mockedVFileChild3 + + val mockedChildrenNodes = listOf>(mockedDataset1, mockedDataset2, mockedDataset3) + + should("sort by name ascending") { + + val sortQueryKeys = listOf(SortQueryKeys.DATASET_NAME) + every { classUnderTest.currentSortQueryKeysList } returns listOf(SortQueryKeys.DATASET_NAME, SortQueryKeys.ASCENDING) + + val expected = listOf(mockedDataset1, mockedDataset2, mockedDataset3) + val actual = classUnderTest.sortChildrenNodes(mockedChildrenNodes, sortQueryKeys) + + assertSoftly { + actual shouldBe expected + } + } + + should("sort by name descending") { + + val sortQueryKeys = listOf(SortQueryKeys.DATASET_NAME) + every { classUnderTest.currentSortQueryKeysList } returns listOf(SortQueryKeys.DATASET_NAME, SortQueryKeys.DESCENDING) + + val expected = listOf(mockedDataset3, mockedDataset2, mockedDataset1) + val actual = classUnderTest.sortChildrenNodes(mockedChildrenNodes, sortQueryKeys) + + assertSoftly { + actual shouldBe expected + } + } + + should("sort by date ascending") { + + val sortQueryKeys = listOf(SortQueryKeys.DATASET_MODIFICATION_DATE) + every { classUnderTest.currentSortQueryKeysList } returns listOf(SortQueryKeys.DATASET_MODIFICATION_DATE, SortQueryKeys.ASCENDING) + + val expected = listOf(mockedDataset2, mockedDataset1, mockedDataset3) + val actual = classUnderTest.sortChildrenNodes(mockedChildrenNodes, sortQueryKeys) + + assertSoftly { + actual shouldBe expected + } + } + + should("sort by date descending") { + + val sortQueryKeys = listOf(SortQueryKeys.DATASET_MODIFICATION_DATE) + every { classUnderTest.currentSortQueryKeysList } returns listOf(SortQueryKeys.DATASET_MODIFICATION_DATE, SortQueryKeys.DESCENDING) + + val expected = listOf(mockedDataset3, mockedDataset1, mockedDataset2) + val actual = classUnderTest.sortChildrenNodes(mockedChildrenNodes, sortQueryKeys) + + assertSoftly { + actual shouldBe expected + } + } + + should("sort by type ascending") { + + val sortQueryKeys = listOf(SortQueryKeys.DATASET_TYPE) + every { classUnderTest.currentSortQueryKeysList } returns listOf(SortQueryKeys.DATASET_TYPE, SortQueryKeys.ASCENDING) + + val expected = listOf(mockedDataset3, mockedDataset1, mockedDataset2) + val actual = classUnderTest.sortChildrenNodes(mockedChildrenNodes, sortQueryKeys) + + assertSoftly { + actual shouldBe expected + } + } + + should("sort by type descending") { + + val sortQueryKeys = listOf(SortQueryKeys.DATASET_TYPE) + every { classUnderTest.currentSortQueryKeysList } returns listOf(SortQueryKeys.DATASET_TYPE, SortQueryKeys.DESCENDING) + + val expected = listOf(mockedDataset3, mockedDataset2, mockedDataset1) + val actual = classUnderTest.sortChildrenNodes(mockedChildrenNodes, sortQueryKeys) + + assertSoftly { + actual shouldBe expected + } + } + + should("return unsorted nodes when passing null sort key") { + + val sortQueryKeys = listOf() + + val actual = classUnderTest.sortChildrenNodes(mockedChildrenNodes, sortQueryKeys) + + assertSoftly { + actual shouldBe mockedChildrenNodes + } + } + + should("return unsorted nodes when passing invalid sort key") { + + val mockedChildrenNodesForThisTest = listOf>(mockedDataset1, mockedDataset2, mockedDataset3, unexpectedNode) + val sortQueryKeys = listOf(SortQueryKeys.JOB_NAME) + + val actual = classUnderTest.sortChildrenNodes(mockedChildrenNodesForThisTest, sortQueryKeys) + + assertSoftly { + actual shouldBe mockedChildrenNodesForThisTest + } + } + } + } + +}) diff --git a/src/test/kotlin/org/zowe/explorer/explorer/ui/FileFetchNodeTestSpec.kt b/src/test/kotlin/org/zowe/explorer/explorer/ui/FileFetchNodeTestSpec.kt new file mode 100644 index 000000000..ec4ca601c --- /dev/null +++ b/src/test/kotlin/org/zowe/explorer/explorer/ui/FileFetchNodeTestSpec.kt @@ -0,0 +1,141 @@ +/* + * This program and the accompanying materials are made available under the terms of the + * Eclipse Public License v2.0 which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-v20.html + * + * SPDX-License-Identifier: EPL-2.0 + * + * Copyright IBA Group 2020 + */ + +package org.zowe.explorer.explorer.ui + +import com.intellij.ide.projectView.PresentationData +import com.intellij.openapi.application.ApplicationManager +import com.intellij.openapi.project.Project +import com.intellij.openapi.vfs.VirtualFile +import com.intellij.testFramework.LightProjectDescriptor +import com.intellij.testFramework.fixtures.IdeaTestFixtureFactory +import com.intellij.testFramework.fixtures.impl.LightTempDirTestFixtureImpl +import com.intellij.ui.SimpleTextAttributes +import org.zowe.explorer.config.connect.ConnectionConfig +import org.zowe.explorer.config.ws.DSMask +import org.zowe.explorer.dataops.DataOpsManager +import org.zowe.explorer.dataops.Query +import org.zowe.explorer.dataops.RemoteQuery +import org.zowe.explorer.dataops.fetch.DatasetFileFetchProvider +import org.zowe.explorer.dataops.fetch.FileFetchProvider +import org.zowe.explorer.explorer.FileExplorer +import org.zowe.explorer.explorer.FilesWorkingSetImpl +import org.zowe.explorer.testutils.WithApplicationShouldSpec +import org.zowe.explorer.testutils.testServiceImpl.TestDataOpsManagerImpl +import org.zowe.explorer.utils.service +import io.kotest.core.spec.style.ShouldSpec +import io.mockk.* +import java.time.LocalDateTime + +class FileFetchNodeTestSpec: WithApplicationShouldSpec({ + + afterSpec { + clearAllMocks() + } + + context("refresh date test spec") { + + val dataOpsManagerService = + ApplicationManager.getApplication().service() as TestDataOpsManagerImpl + val componentManager = dataOpsManagerService.componentManager + val datasetFileFetchProvider = mockk() + + val queryMock = mockk>() + val lastRefreshDate = LocalDateTime.of(2023, 12, 30, 10, 0, 0) + + val presentationMock = mockk() + val mockedMask = mockk() + val mockedProject = mockk() + val mockedExplorerTreeNodeParent = mockk() + val mockedWorkingSet = mockk() + val mockedExplorer = mockk() + val mockedExplorerTreeStructure = mockk() + + every { mockedWorkingSet.explorer } returns mockedExplorer + every { mockedExplorer.componentManager } returns mockk() + every { mockedExplorer.componentManager.service() } returns dataOpsManagerService + every { mockedExplorerTreeStructure.registerNode(any()) } just Runs + every { mockedWorkingSet.connectionConfig } returns mockk() + + dataOpsManagerService.testInstance = object: TestDataOpsManagerImpl(componentManager) { + override fun , File : VirtualFile> getFileFetchProvider( + requestClass: Class, + queryClass: Class>, + vFileClass: Class + ): FileFetchProvider { + return datasetFileFetchProvider as FileFetchProvider + } + } + + val classUnderTest = DSMaskNode(mockedMask, mockedProject, mockedExplorerTreeNodeParent, mockedWorkingSet, mockedExplorerTreeStructure) + + context("updateRefreshDateAndTime") { + should("should update node presentation with correct refresh date and time given valid query") { + //given + val text = " latest refresh: 30 DECEMBER 10:00:00" + every { presentationMock.addText(any(), any()) } just Runs + every { datasetFileFetchProvider.getRealQueryInstance(any()) } returns queryMock + every { datasetFileFetchProvider.findCacheRefreshDateIfPresent(any()) } returns lastRefreshDate + + //when + classUnderTest.updateRefreshDateAndTime(presentationMock) + + //then + verify(exactly = 1) { datasetFileFetchProvider.findCacheRefreshDateIfPresent(queryMock) } + verify(exactly = 1) { presentationMock.addText(text, SimpleTextAttributes.GRAYED_ATTRIBUTES) } + clearMocks(presentationMock, verificationMarks = true) + + } + + should("should update node presentation with correct refresh date and time given valid query if no real instance found") { + //given + val text = " latest refresh: 30 DECEMBER 10:00:00" + every { presentationMock.addText(any(), any()) } just Runs + every { datasetFileFetchProvider.getRealQueryInstance(any()) } returns null + every { datasetFileFetchProvider.findCacheRefreshDateIfPresent(any()) } returns lastRefreshDate + + //when + classUnderTest.updateRefreshDateAndTime(presentationMock) + + //then + verify(exactly = 1) { presentationMock.addText(text, SimpleTextAttributes.GRAYED_ATTRIBUTES) } + clearMocks(presentationMock, verificationMarks = true) + } + + should("should not update presentation for node if no refresh date found") { + //given + every { datasetFileFetchProvider.getRealQueryInstance(any()) } returns null + every { datasetFileFetchProvider.findCacheRefreshDateIfPresent(any()) } returns null + + //when + classUnderTest.updateRefreshDateAndTime(presentationMock) + + //then + verify { presentationMock wasNot Called } + clearMocks(presentationMock, verificationMarks = true) + } + + should("should update node presentation with Out-Of-Sync text if no valid query") { + //given + val text = " Out of sync" + every { mockedWorkingSet.connectionConfig } returns null + every { presentationMock.addText(any(), any()) } just Runs + every { datasetFileFetchProvider.getRealQueryInstance(any()) } returns null + + //when + classUnderTest.updateRefreshDateAndTime(presentationMock) + + //then + verify(exactly = 1) { presentationMock.addText(text, SimpleTextAttributes.GRAYED_ATTRIBUTES) } + } + } + unmockkAll() + } +}) diff --git a/src/test/kotlin/org/zowe/explorer/explorer/ui/JesFilterNodeTestSpec.kt b/src/test/kotlin/org/zowe/explorer/explorer/ui/JesFilterNodeTestSpec.kt new file mode 100644 index 000000000..1a5ad750a --- /dev/null +++ b/src/test/kotlin/org/zowe/explorer/explorer/ui/JesFilterNodeTestSpec.kt @@ -0,0 +1,276 @@ +/* + * This program and the accompanying materials are made available under the terms of the + * Eclipse Public License v2.0 which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-v20.html + * + * SPDX-License-Identifier: EPL-2.0 + * + * Copyright IBA Group 2020 + */ + +package org.zowe.explorer.explorer.ui + +import com.intellij.openapi.application.ApplicationManager +import com.intellij.openapi.project.Project +import com.intellij.openapi.vfs.VirtualFile +import org.zowe.explorer.config.ws.JobsFilter +import org.zowe.explorer.dataops.DataOpsManager +import org.zowe.explorer.dataops.attributes.* +import org.zowe.explorer.dataops.sort.SortQueryKeys +import org.zowe.explorer.explorer.JesExplorer +import org.zowe.explorer.explorer.JesWorkingSetImpl +import org.zowe.explorer.testutils.WithApplicationShouldSpec +import org.zowe.explorer.testutils.testServiceImpl.TestDataOpsManagerImpl +import org.zowe.explorer.utils.clearOldKeysAndAddNew +import org.zowe.explorer.utils.service +import org.zowe.explorer.vfs.MFVirtualFile +import io.kotest.assertions.assertSoftly +import io.kotest.matchers.collections.shouldContainExactly +import io.mockk.* +import org.zowe.kotlinsdk.Job + +class JesFilterNodeTestSpec : WithApplicationShouldSpec({ + + afterSpec { + clearAllMocks() + unmockkAll() + } + + context("explorer module: ui/JesFilterNode") { + + val mockedJobsFilter = mockk() + val mockedProject = mockk() + val mockedParent = mockk() + val mockedWorkingSet = mockk() + val mockedExplorer = mockk() + val mockedExplorerTreeStructure = mockk() + + every { mockedWorkingSet.explorer } returns mockedExplorer + every { mockedExplorerTreeStructure.registerNode(any()) } just Runs + + val classUnderTest = spyk(JesFilterNode(mockedJobsFilter, mockedProject, mockedParent, mockedWorkingSet, mockedExplorerTreeStructure)) + + context("sort children nodes") { + + val virtualFile1 = mockk() + val virtualFile2 = mockk() + val virtualFile3 = mockk() + val jobAttributes1 = mockk() + val jobAttributes2 = mockk() + val jobAttributes3 = mockk() + val jobInfo1 = Job( + "id1", + "name1", + null, + "ARST", + Job.Status.OUTPUT, + Job.JobType.JOB, + null, + null, + "url1", + "filesUrl1", + null, + 1, + "phase1", + listOf(), + null, + null, + null, + null, + "2024-01-02", + "2024-01-02" + ) + val jobInfo2 = Job( + "id2", + "name2", + null, + "DLIS", + Job.Status.ACTIVE, + Job.JobType.JOB, + null, + null, + "url2", + "filesUrl2", + null, + 2, + "phase2", + listOf(), + null, + null, + null, + null, + "2024-01-04", + "2024-01-04" + ) + val jobInfo3 = Job( + "id3", + "name3", + null, + "ZOSMFAD", + Job.Status.INPUT, + Job.JobType.JOB, + null, + null, + "url3", + "filesUrl3", + null, + 3, + "phase3", + listOf(), + null, + null, + null, + null, + "2024-01-01", + "2024-01-06" + ) + val jobNode1 = mockk() + val jobNode2 = mockk() + val jobNode3 = mockk() + every { jobNode1.value } returns virtualFile1 + every { jobNode2.value } returns virtualFile2 + every { jobNode3.value } returns virtualFile3 + every { jobAttributes1.jobInfo } returns jobInfo1 + every { jobAttributes2.jobInfo } returns jobInfo2 + every { jobAttributes3.jobInfo } returns jobInfo3 + + val nodeToAttributesMap = mutableMapOf(Pair(virtualFile1, jobAttributes1), Pair(virtualFile2, jobAttributes2), Pair(virtualFile3, jobAttributes3)) + + val listToSort = listOf(jobNode1, jobNode2, jobNode3) + + val dataOpsManagerService = ApplicationManager.getApplication().service() as TestDataOpsManagerImpl + dataOpsManagerService.testInstance = object : TestDataOpsManagerImpl(ApplicationManager.getApplication()) { + override fun tryToGetAttributes(file: VirtualFile): FileAttributes? { + return nodeToAttributesMap[file] + } + } + + should("return children nodes sorted by JOB NAME in ascending order") { + // given + val sortKeys = listOf(SortQueryKeys.JOB_NAME, SortQueryKeys.ASCENDING) + val expected = listOf(jobNode1, jobNode2, jobNode3) + + // when + val actual = classUnderTest.sortChildrenNodes(listToSort, sortKeys) + + //then + assertSoftly { + actual shouldContainExactly expected + } + } + + should("return children nodes sorted by JOB OWNER in ascending order") { + // given + val sortKeys = listOf(SortQueryKeys.JOB_OWNER, SortQueryKeys.ASCENDING) + val expected = listOf(jobNode1, jobNode2, jobNode3) + + // when + val actual = classUnderTest.sortChildrenNodes(listToSort, sortKeys) + + //then + assertSoftly { + actual shouldContainExactly expected + } + } + + should("return children nodes sorted by JOB STATUS in ascending order") { + // given + val sortKeys = listOf(SortQueryKeys.JOB_STATUS, SortQueryKeys.ASCENDING) + val expected = listOf(jobNode2, jobNode3, jobNode1) + + // when + val actual = classUnderTest.sortChildrenNodes(listToSort, sortKeys) + + //then + assertSoftly { + actual shouldContainExactly expected + } + } + + should("return children nodes sorted by JOB ID in ascending order") { + // given + val sortKeys = listOf(SortQueryKeys.JOB_ID, SortQueryKeys.ASCENDING) + val expected = listOf(jobNode1, jobNode2, jobNode3) + + // when + val actual = classUnderTest.sortChildrenNodes(listToSort, sortKeys) + + //then + assertSoftly { + actual shouldContainExactly expected + } + } + + should("return children nodes sorted by JOB CREATION DATE in ascending order") { + // given + val sortKeys = listOf(SortQueryKeys.JOB_CREATION_DATE, SortQueryKeys.ASCENDING) + val expected = listOf(jobNode3, jobNode1, jobNode2) + + // when + val actual = classUnderTest.sortChildrenNodes(listToSort, sortKeys) + + //then + assertSoftly { + actual shouldContainExactly expected + } + } + + should("return children nodes sorted by JOB COMPLETION DATE in ascending order") { + // given + val sortKeys = listOf(SortQueryKeys.JOB_COMPLETION_DATE, SortQueryKeys.ASCENDING) + val expected = listOf(jobNode1, jobNode2, jobNode3) + + // when + val actual = classUnderTest.sortChildrenNodes(listToSort, sortKeys) + + //then + assertSoftly { + actual shouldContainExactly expected + } + } + + should("return children nodes sorted by invalid sort key") { + // given + val sortKeys = listOf(SortQueryKeys.FILE_NAME, SortQueryKeys.ASCENDING) + val expected = listOf(jobNode1, jobNode2, jobNode3) + + // when + val actual = classUnderTest.sortChildrenNodes(listToSort, sortKeys) + + //then + assertSoftly { + actual shouldContainExactly expected + } + } + + should("return children nodes without sorting if sort keys are null") { + // given + val sortKeys = listOf() + val expected = listOf(jobNode1, jobNode2, jobNode3) + + // when + val actual = classUnderTest.sortChildrenNodes(listToSort, sortKeys) + + //then + assertSoftly { + actual shouldContainExactly expected + } + } + + should("return children nodes sorted by JOB NAME in descending order") { + // given + classUnderTest.currentSortQueryKeysList.clearOldKeysAndAddNew(SortQueryKeys.DESCENDING) + val sortKeys = listOf(SortQueryKeys.JOB_NAME) + val expected = listOf(jobNode3, jobNode2, jobNode1) + + // when + val actual = classUnderTest.sortChildrenNodes(listToSort, sortKeys) + + //then + assertSoftly { + actual shouldContainExactly expected + } + } + } + } +}) diff --git a/src/test/kotlin/org/zowe/explorer/explorer/ui/LibraryNodeTestSpec.kt b/src/test/kotlin/org/zowe/explorer/explorer/ui/LibraryNodeTestSpec.kt new file mode 100644 index 000000000..5b3d91e1f --- /dev/null +++ b/src/test/kotlin/org/zowe/explorer/explorer/ui/LibraryNodeTestSpec.kt @@ -0,0 +1,175 @@ +/* + * This program and the accompanying materials are made available under the terms of the + * Eclipse Public License v2.0 which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-v20.html + * + * SPDX-License-Identifier: EPL-2.0 + * + * Copyright IBA Group 2020 + */ + +package org.zowe.explorer.explorer.ui + +import com.intellij.ide.util.treeView.AbstractTreeNode +import com.intellij.openapi.application.ApplicationManager +import com.intellij.openapi.project.Project +import com.intellij.openapi.vfs.VirtualFile +import org.zowe.explorer.dataops.DataOpsManager +import org.zowe.explorer.dataops.attributes.* +import org.zowe.explorer.dataops.sort.SortQueryKeys +import org.zowe.explorer.explorer.FileExplorer +import org.zowe.explorer.explorer.FilesWorkingSetImpl +import org.zowe.explorer.testutils.WithApplicationShouldSpec +import org.zowe.explorer.testutils.testServiceImpl.TestDataOpsManagerImpl +import org.zowe.explorer.utils.service +import org.zowe.explorer.vfs.MFVirtualFile +import io.kotest.assertions.assertSoftly +import io.kotest.matchers.shouldBe +import io.mockk.* +import org.zowe.kotlinsdk.Member + +class LibraryNodeTestSpec : WithApplicationShouldSpec({ + + afterSpec { + clearAllMocks() + unmockkAll() + } + + context("explorer module: ui/LibraryNode") { + val mockedLibrary = mockk() + val mockedProject = mockk() + val mockedExplorerTreeNodeParent = mockk() + val mockedWorkingSet = mockk() + val mockedExplorer = mockk() + val mockedExplorerTreeStructure = mockk() + + every { mockedWorkingSet.explorer } returns mockedExplorer + every { mockedExplorerTreeStructure.registerNode(any()) } just Runs + + val classUnderTest = spyk(LibraryNode(mockedLibrary, mockedProject, mockedExplorerTreeNodeParent, mockedWorkingSet, mockedExplorerTreeStructure)) + + context("sort children nodes") { + val mockedVFileChild1 = mockk() + val mockedVFileChild2 = mockk() + val mockedVFileChild3 = mockk() + val mockedAttributes1 = mockk() + val mockedAttributes2 = mockk() + val mockedAttributes3 = mockk() + val memberInfo1 = mockk() + val memberInfo2 = mockk() + val memberInfo3 = mockk() + + val nodeToAttributesMap = mutableMapOf(Pair(mockedVFileChild1, mockedAttributes1), Pair(mockedVFileChild2, mockedAttributes2), Pair(mockedVFileChild3, mockedAttributes3)) + + val dataOpsManagerService = ApplicationManager.getApplication().service() as TestDataOpsManagerImpl + dataOpsManagerService.testInstance = object : TestDataOpsManagerImpl(ApplicationManager.getApplication()) { + override fun tryToGetAttributes(file: VirtualFile): FileAttributes? { + return nodeToAttributesMap[file] + } + } + + beforeEach { + every { mockedAttributes1.info } returns memberInfo1 + every { mockedAttributes2.info } returns memberInfo2 + every { mockedAttributes3.info } returns memberInfo3 + every { mockedAttributes1.info.name } returns "AAAA" + every { mockedAttributes2.info.name } returns "BBBB" + every { mockedAttributes3.info.name } returns "CCCC" + every { mockedAttributes1.info.modificationDate } returns "2024/01/10" + every { mockedAttributes2.info.modificationDate } returns "2024/01/09" + every { mockedAttributes3.info.modificationDate } returns "2024/02/02" + } + + val mockedMember1 = spyk( + FileLikeDatasetNode(mockedVFileChild1, mockedProject, mockedExplorerTreeNodeParent, mockedWorkingSet, mockedExplorerTreeStructure) + ) + val mockedMember2 = spyk( + FileLikeDatasetNode(mockedVFileChild2, mockedProject, mockedExplorerTreeNodeParent, mockedWorkingSet, mockedExplorerTreeStructure) + ) + val mockedMember3 = spyk( + FileLikeDatasetNode(mockedVFileChild3, mockedProject, mockedExplorerTreeNodeParent, mockedWorkingSet, mockedExplorerTreeStructure) + ) + + every { mockedMember1.virtualFile } returns mockedVFileChild1 + every { mockedMember2.virtualFile } returns mockedVFileChild2 + every { mockedMember3.virtualFile } returns mockedVFileChild3 + + val mockedChildrenNodes = listOf>(mockedMember1, mockedMember2, mockedMember3) + + should("sort by name ascending") { + + val sortQueryKeys = listOf(SortQueryKeys.MEMBER_NAME) + every { classUnderTest.currentSortQueryKeysList } returns listOf(SortQueryKeys.MEMBER_NAME, SortQueryKeys.ASCENDING) + + val expected = listOf(mockedMember1, mockedMember2, mockedMember3) + val actual = classUnderTest.sortChildrenNodes(mockedChildrenNodes, sortQueryKeys) + + assertSoftly { + actual shouldBe expected + } + } + + should("sort by name descending") { + + val sortQueryKeys = listOf(SortQueryKeys.MEMBER_NAME) + every { classUnderTest.currentSortQueryKeysList } returns listOf(SortQueryKeys.MEMBER_NAME, SortQueryKeys.DESCENDING) + + val expected = listOf(mockedMember3, mockedMember2, mockedMember1) + val actual = classUnderTest.sortChildrenNodes(mockedChildrenNodes, sortQueryKeys) + + assertSoftly { + actual shouldBe expected + } + } + + should("sort by date ascending") { + + val sortQueryKeys = listOf(SortQueryKeys.MEMBER_MODIFICATION_DATE) + every { classUnderTest.currentSortQueryKeysList } returns listOf(SortQueryKeys.MEMBER_MODIFICATION_DATE, SortQueryKeys.ASCENDING) + + val expected = listOf(mockedMember2, mockedMember1, mockedMember3) + val actual = classUnderTest.sortChildrenNodes(mockedChildrenNodes, sortQueryKeys) + + assertSoftly { + actual shouldBe expected + } + } + + should("sort by date descending") { + + val sortQueryKeys = listOf(SortQueryKeys.MEMBER_MODIFICATION_DATE) + every { classUnderTest.currentSortQueryKeysList } returns listOf(SortQueryKeys.MEMBER_MODIFICATION_DATE, SortQueryKeys.DESCENDING) + + val expected = listOf(mockedMember3, mockedMember1, mockedMember2) + val actual = classUnderTest.sortChildrenNodes(mockedChildrenNodes, sortQueryKeys) + + assertSoftly { + actual shouldBe expected + } + } + + should("return unsorted nodes when passing null sort key") { + + val sortQueryKeys = listOf() + + val actual = classUnderTest.sortChildrenNodes(mockedChildrenNodes, sortQueryKeys) + + assertSoftly { + actual shouldBe mockedChildrenNodes + } + } + + should("return unsorted nodes when passing invalid sort key") { + + val sortQueryKeys = listOf(SortQueryKeys.JOB_NAME) + + val actual = classUnderTest.sortChildrenNodes(mockedChildrenNodes, sortQueryKeys) + + assertSoftly { + actual shouldBe mockedChildrenNodes + } + } + } + } + +}) diff --git a/src/test/kotlin/org/zowe/explorer/explorer/ui/UssDirNodeTestSpec.kt b/src/test/kotlin/org/zowe/explorer/explorer/ui/UssDirNodeTestSpec.kt new file mode 100644 index 000000000..4bef644c8 --- /dev/null +++ b/src/test/kotlin/org/zowe/explorer/explorer/ui/UssDirNodeTestSpec.kt @@ -0,0 +1,189 @@ +/* + * This program and the accompanying materials are made available under the terms of the + * Eclipse Public License v2.0 which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-v20.html + * + * SPDX-License-Identifier: EPL-2.0 + * + * Copyright IBA Group 2020 + */ + +package org.zowe.explorer.explorer.ui + +import com.intellij.ide.util.treeView.AbstractTreeNode +import com.intellij.openapi.project.Project +import org.zowe.explorer.config.ws.UssPath +import org.zowe.explorer.dataops.DataOpsManager +import org.zowe.explorer.dataops.attributes.RemoteUssAttributes +import org.zowe.explorer.dataops.attributes.RemoteUssAttributesService +import org.zowe.explorer.dataops.getAttributesService +import org.zowe.explorer.dataops.sort.SortQueryKeys +import org.zowe.explorer.explorer.FileExplorer +import org.zowe.explorer.explorer.FilesWorkingSetImpl +import org.zowe.explorer.testutils.WithApplicationShouldSpec +import org.zowe.explorer.utils.service +import org.zowe.explorer.vfs.MFVirtualFile +import io.kotest.assertions.assertSoftly +import io.kotest.matchers.shouldBe +import io.mockk.* + +class UssDirNodeTestSpec : WithApplicationShouldSpec({ + + afterSpec { + clearAllMocks() + unmockkAll() + } + + context("explorer module: ui/UssDirNode") { + val mockedPath = mockk("USS//test_path/ZOSMFAD/test_dir") + val mockedProject = mockk() + val mockedExplorerTreeNodeParent = mockk() + val mockedWorkingSet = mockk() + val mockedExplorer = mockk() + val mockedExplorerTreeStructure = mockk() + val isRootNode = false + + val mockedUssAttributesService = mockk() + + every { mockedWorkingSet.explorer } returns mockedExplorer + every { mockedExplorer.componentManager } returns mockk() + every { mockedExplorer.componentManager.service() } returns mockk() + every { mockedExplorer.componentManager.service().getAttributesService() } returns mockedUssAttributesService + every { mockedExplorerTreeStructure.registerNode(any()) } just Runs + + context("sort children nodes") { + val mockedVFile = mockk() + val childNode3 = mockk() + val childNode4 = mockk() + val unexpectedNode = mockk() + val mockedVFileChild1 = mockk() + val mockedVFileChild2 = mockk() + val mockedAttributes1 = mockk() + val mockedAttributes2 = mockk() + val mockedAttributes3 = mockk() + val mockedAttributes4 = mockk() + + beforeEach { + every { mockedVFile.filenameInternal } returns "test_dir" + every { mockedVFileChild1.filenameInternal } returns "a_dir" + every { mockedVFileChild2.filenameInternal } returns "b_dir" + every { childNode3.virtualFile } returns mockk() + every { childNode3.virtualFile.filenameInternal } returns "a_file" + every { childNode4.virtualFile } returns mockk() + every { childNode4.virtualFile.filenameInternal } returns "b_file" + + every { mockedUssAttributesService.getAttributes(mockedVFileChild1) } returns mockedAttributes1 + every { mockedUssAttributesService.getAttributes(mockedVFileChild2) } returns mockedAttributes2 + every { mockedUssAttributesService.getAttributes(childNode3.virtualFile) } returns mockedAttributes3 + every { mockedUssAttributesService.getAttributes(childNode4.virtualFile) } returns mockedAttributes4 + + every { mockedAttributes1.modificationTime } returns "2022/12/11" + every { mockedAttributes2.modificationTime } returns "2022/12/10" + every { mockedAttributes3.modificationTime } returns "2022/12/09" + every { mockedAttributes4.modificationTime } returns "2022/12/08" + } + + val mockedUssDirNodeChild1 = spyk( + UssDirNode(mockedPath, mockedProject, mockedExplorerTreeNodeParent, mockedWorkingSet, mockedExplorerTreeStructure, mockedVFileChild1, isRootNode) + ) + val mockedUssDirNodeChild2 = spyk( + UssDirNode(mockedPath, mockedProject, mockedExplorerTreeNodeParent, mockedWorkingSet, mockedExplorerTreeStructure, mockedVFileChild2, isRootNode) + ) + val mockedUssDirNode = spyk( + UssDirNode(mockedPath, mockedProject, mockedExplorerTreeNodeParent, mockedWorkingSet, mockedExplorerTreeStructure, mockedVFile, isRootNode) + ) + + val mockedChildrenNodes = listOf>(mockedUssDirNodeChild1, mockedUssDirNodeChild2, childNode3, childNode4, unexpectedNode) + + should("sort by name ascending") { + + val sortQueryKeys = listOf(SortQueryKeys.FILE_NAME, SortQueryKeys.ASCENDING) + mockkObject(sortQueryKeys) + + val expected = listOf(unexpectedNode, mockedUssDirNodeChild1, childNode3, mockedUssDirNodeChild2, childNode4) + val actual = mockedUssDirNode.sortChildrenNodes(mockedChildrenNodes, sortQueryKeys) + + assertSoftly { + actual shouldBe expected + } + } + + should("sort by name descending") { + + val sortQueryKeys = listOf(SortQueryKeys.FILE_NAME, SortQueryKeys.DESCENDING) + mockkObject(sortQueryKeys) + + val expected = listOf(childNode4, mockedUssDirNodeChild2, childNode3, mockedUssDirNodeChild1, unexpectedNode) + val actual = mockedUssDirNode.sortChildrenNodes(mockedChildrenNodes, sortQueryKeys) + + assertSoftly { + actual shouldBe expected + } + } + + should("sort by type ascending") { + + val sortQueryKeys = listOf(SortQueryKeys.FILE_TYPE, SortQueryKeys.ASCENDING) + mockkObject(sortQueryKeys) + + val expected = listOf(mockedUssDirNodeChild1, mockedUssDirNodeChild2, childNode3, childNode4) + val actual = mockedUssDirNode.sortChildrenNodes(mockedChildrenNodes, sortQueryKeys) + + assertSoftly { + actual shouldBe expected + } + } + + should("sort by type descending") { + + val sortQueryKeys = listOf(SortQueryKeys.FILE_TYPE, SortQueryKeys.DESCENDING) + mockkObject(sortQueryKeys) + + val expected = listOf(mockedUssDirNodeChild2, mockedUssDirNodeChild1, childNode4, childNode3) + val actual = mockedUssDirNode.sortChildrenNodes(mockedChildrenNodes, sortQueryKeys) + + assertSoftly { + actual shouldBe expected + } + } + + should("sort by date ascending") { + + val sortQueryKeys = listOf(SortQueryKeys.FILE_MODIFICATION_DATE, SortQueryKeys.ASCENDING) + mockkObject(sortQueryKeys) + + val expected = listOf(unexpectedNode, childNode4, childNode3, mockedUssDirNodeChild2, mockedUssDirNodeChild1) + val actual = mockedUssDirNode.sortChildrenNodes(mockedChildrenNodes, sortQueryKeys) + + assertSoftly { + actual shouldBe expected + } + } + + should("sort by date descending") { + + val sortQueryKeys = listOf(SortQueryKeys.FILE_MODIFICATION_DATE, SortQueryKeys.DESCENDING) + mockkObject(sortQueryKeys) + + val expected = listOf(mockedUssDirNodeChild1, mockedUssDirNodeChild2, childNode3, childNode4, unexpectedNode) + val actual = mockedUssDirNode.sortChildrenNodes(mockedChildrenNodes, sortQueryKeys) + + assertSoftly { + actual shouldBe expected + } + } + + should("return unsorted nodes when passing invalid sort key") { + + val sortQueryKeys = listOf(SortQueryKeys.JOB_NAME, SortQueryKeys.DESCENDING) + mockkObject(sortQueryKeys) + + val actual = mockedUssDirNode.sortChildrenNodes(mockedChildrenNodes, sortQueryKeys) + + assertSoftly { + actual shouldBe mockedChildrenNodes + } + } + } + } +}) diff --git a/src/test/kotlin/org/zowe/explorer/utils/UtilsTestSpec.kt b/src/test/kotlin/org/zowe/explorer/utils/UtilsTestSpec.kt index 6057e69a1..9371d1a17 100644 --- a/src/test/kotlin/org/zowe/explorer/utils/UtilsTestSpec.kt +++ b/src/test/kotlin/org/zowe/explorer/utils/UtilsTestSpec.kt @@ -13,6 +13,7 @@ package org.zowe.explorer.utils import com.intellij.openapi.progress.ProgressIndicator import com.intellij.openapi.progress.TaskInfo import com.intellij.openapi.ui.ValidationInfo +import com.intellij.openapi.util.text.StringUtil import com.intellij.openapi.wm.ex.ProgressIndicatorEx import com.intellij.ui.components.JBTextField import org.zowe.explorer.config.ConfigStateV2 @@ -24,6 +25,7 @@ import org.zowe.explorer.config.ws.JobsFilter import org.zowe.explorer.config.ws.MaskStateWithWS import org.zowe.explorer.config.ws.UssPath import org.zowe.explorer.config.ws.WorkingSetConfig +import org.zowe.explorer.dataops.sort.SortQueryKeys import org.zowe.explorer.explorer.FilesWorkingSet import org.zowe.explorer.explorer.ui.NodeData import org.zowe.explorer.explorer.ui.UssDirNode @@ -32,6 +34,7 @@ import org.zowe.explorer.vfs.MFVirtualFile import org.zowe.explorer.vfs.MFVirtualFileSystem import io.kotest.assertions.assertSoftly import io.kotest.core.spec.style.ShouldSpec +import io.kotest.matchers.collections.shouldContainExactly import io.kotest.matchers.longs.shouldBeGreaterThanOrEqual import io.kotest.matchers.shouldBe import io.mockk.every @@ -41,11 +44,13 @@ import io.mockk.spyk import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext import org.junit.jupiter.api.Assertions.* +import org.junit.platform.commons.util.StringUtils import org.zowe.kotlinsdk.annotations.ZVersion import retrofit2.Call import retrofit2.Response import java.time.Duration import java.time.Instant.now +import java.time.LocalDateTime import java.util.stream.Stream import javax.swing.JTextField @@ -883,5 +888,69 @@ class UtilsTestSpec : ShouldSpec({ duration.toMillis() shouldBeGreaterThanOrEqual 500 } } + + should("return a human readable date format given valid LocalDateTime instance") { + //given + val actualLocalDate = LocalDateTime.of(2023, 12, 30, 10, 0, 0) + val expectedString = "30 DECEMBER 10:00:00" + //when + val actualString = actualLocalDate.toHumanReadableFormat() + //then + assertSoftly { + actualString shouldBe expectedString + } + } + + should("return this list cleared and one new element added, given the input list and another list") { + //given + val receiver = mutableListOf("AAA", "BBB", "CCC") + val another = listOf("DDD") + val expectedList = listOf("DDD") + //when + receiver.clearAndMergeWith(another) + //then + assertSoftly { + receiver shouldContainExactly expectedList + } + } + + should("return this list cleared and new sort key added, given the input list and TYPED sort key to add") { + //given + val receiver = mutableListOf(SortQueryKeys.JOB_NAME, SortQueryKeys.DESCENDING) + val toAdd = SortQueryKeys.JOB_STATUS + val expectedList = listOf(SortQueryKeys.DESCENDING, SortQueryKeys.JOB_STATUS) + //when + receiver.clearOldKeysAndAddNew(toAdd) + //then + assertSoftly { + receiver shouldContainExactly expectedList + } + } + + should("return this list cleared and new sort key added, given the input list and ORDERING sort key to add") { + //given + val receiver = mutableListOf(SortQueryKeys.JOB_NAME, SortQueryKeys.DESCENDING) + val toAdd = SortQueryKeys.ASCENDING + val expectedList = listOf(SortQueryKeys.JOB_NAME, SortQueryKeys.ASCENDING) + //when + receiver.clearOldKeysAndAddNew(toAdd) + //then + assertSoftly { + receiver shouldContainExactly expectedList + } + } + + should("return this without modifications, given the input list and null key to add") { + //given + val receiver = mutableListOf(SortQueryKeys.JOB_NAME, SortQueryKeys.DESCENDING) + val toAdd = null + val expectedList = listOf(SortQueryKeys.JOB_NAME, SortQueryKeys.DESCENDING) + //when + receiver.clearOldKeysAndAddNew(toAdd) + //then + assertSoftly { + receiver shouldContainExactly expectedList + } + } } }) diff --git a/src/uiTest/kotlin/Data.kt b/src/uiTest/kotlin/Data.kt new file mode 100644 index 000000000..920bbc732 --- /dev/null +++ b/src/uiTest/kotlin/Data.kt @@ -0,0 +1,246 @@ +/* + * This program and the accompanying materials are made available under the terms of the + * Eclipse Public License v2.0 which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-v20.html + * + * SPDX-License-Identifier: EPL-2.0 + * + * Copyright IBA Group 2020 + */ +package workingset + +import auxiliary.* + +//Global constants +const val PROJECT_NAME = "untitled" +const val REMOTE_ROBOT_URL = "http://127.0.0.1" + +//Allocation unit shorts +const val TRACKS_ALLOCATION_UNIT_SHORT = "TRK" + +//Button text +const val NO_TEXT = "No" +const val OK_TEXT = "OK" +const val YES_TEXT = "Yes" +const val DELETE_TEXT = "Delete" +const val CANCEL_TEXT = "Cancel" + +// Datasets types +const val PO_ORG_FULL = "Partitioned (PO)" +const val PO_ORG_SHORT = "PO" +const val SEQUENTIAL_ORG_FULL = "Sequential (PS)" +const val SEQUENTIAL_ORG_SHORT = "PS" +const val POE_ORG_FULL = "Partitioned Extended (PO-E)" +const val POE_ORG_SHORT = "POE" +const val PDS_TYPE = "PDS" + +//rename dataset +const val DATASET_FOR_RENAME_PROPERTY = "{\"dsorg\":\"PO\",\"alcunit\":\"TRK\",\"primary\":10,\"secondary\":1,\"dirblk\":2,\"recfm\":\"VB\",\"blksize\":6120,\"lrecl\":255, \"migr\":false}" + +//record formats +const val F_RECORD_FORMAT_SHORT = "F" +const val FB_RECORD_FORMAT_SHORT = "FB" +const val V_RECORD_FORMAT_SHORT = "V" +const val VA_RECORD_FORMAT_SHORT = "VA" +const val VB_RECORD_FORMAT_SHORT = "VB" + +//action menu points +const val REFRESH_POINT_TEXT = "Refresh" +const val NEW_POINT_TEXT = "New" +const val WORKING_SET = "Working Set" +const val DATASET_POINT_TEXT = "Dataset" +const val MIGRATE_POINT_TEXT = "Migrate" +const val RECALL_POINT_TEXT = "Recall" +const val RENAME_POINT_TEXT = "Rename" +const val EDIT_POINT_TEXT = "Edit" +const val PROPERTIES_POINT_TEXT = "Properties" +const val MASK_POINT_TEXT = "Mask" + +//Errors messages +const val invalidDatasetNameConstant = "Each name segment (qualifier) is 1 to 8 characters, the first of which must be alphabetic (A to Z) or " + + "national (# @ $). The remaining seven characters are either alphabetic, numeric (0 - 9), national, " + + "a hyphen (-). Name segments are separated by a period (.)" +const val numberGreaterThanOneMsg = "Enter a number greater than or equal to 1" +const val enterValueInCorrectRangeFromOneMsg = "Please enter a number from 1 to 2,147,483,646" +const val enterValueInCorrectRangeFromZeroMsg = "Please enter a number from 0 to 2,147,483,646" +const val enterPositiveNumberMsg = "Enter a positive number" +const val MEMBER_ALREADY_EXISTS = "Member already exists" +const val RENAME_MEMBER_FAILED = "Rename member failed" +const val MEMBER_IN_USE = "Member in use" +const val INVALID_MEMBER_NAME_MESSAGE = "Member name should contain only A-Z a-z 0-9 or national characters" +const val INVALID_MEMBER_NAME_BEGINNING_MESSAGE = "Member name should start with A-Z a-z or national characters" +const val MEMBER_EMPTY_NAME_MESSAGE = "This field must not be blank" +const val DATASET_INVALID_SECTION_MESSAGE = + "Each name segment (qualifier) is 1 to 8 characters, the first of which must be alphabetic (A to Z) or national (# @ \$). The remaining seven characters are either alphabetic, numeric (0 - 9), national, a hyphen (-). Name segments are separated by a period (.)" +const val DATASET_NAME_LENGTH_MESSAGE = "Dataset name cannot exceed 44 characters" +const val ERROR_IN_PLUGIN = "Error in plugin For Mainframe" +const val DATA_SET_RENAME_FAILED_MSG = "Data set rename failed" +const val NO_ITEMS_FOUND_MSG = "No items found" +const val IDENTICAL_MASKS_MESSAGE = "You cannot add several identical masks to table" +const val EMPTY_DATASET_MESSAGE = "You are going to create a Working Set that doesn't fetch anything" +const val INVALID_URL_PORT = "Invalid URL port: \"%s\"" +const val UNIQUE_WORKING_SET_NAME = "You must provide unique working set name. Working Set %s already exists." +const val UNIQUE_MASK = "You must provide unique mask in working set. Working Set \"%s\" already has mask - %s" + +//Migrate options: +const val HMIGRATE_MIGRATE_OPTIONS = "hmigrate" +const val HRECALL_MIGRATE_OPTIONS = "hrecall" + + +//dialog text\patterns + +const val datasetHasBeenCreated = "Dataset %s Has Been Created " + + +//bad alloc params cases +data class InvalidAllocate( + val wsName: String, + val datasetName: String, + val datasetOrganization: String, + val allocationUnit: String, + val primaryAllocation: Int, + val secondaryAllocation: Int, + val directory: Int, + val recordFormat: String, + val recordLength: Int, + val blockSize: Int, + val averageBlockLength: Int, + val message: String +) + +val invalidDatasetNameParams = InvalidAllocate( + "", "A23456789.A", PO_ORG_FULL, "TRK", 10, 1, + 1, "FB", 80, 3200, 0, invalidDatasetNameConstant +) +val invalidPrimaryAllocationParams = InvalidAllocate( + "", "A23.A23", PO_ORG_FULL, "TRK", -2, 0, 1, + "FB", 80, 3200, 0, enterValueInCorrectRangeFromOneMsg) + +val invalidDirectoryParams = InvalidAllocate( + "", "A23.A23", PO_ORG_FULL, "TRK", 10, 0, 0, + "FB", 80, 3200, 0,enterValueInCorrectRangeFromOneMsg + ) +val invalidRecordLengthParams = InvalidAllocate( + "", "A23.A23", PO_ORG_FULL, "TRK", 10, 0, 1, + "FB", 0, 3200, 0,enterValueInCorrectRangeFromOneMsg + ) +val invalidSecondaryAllocationParams = InvalidAllocate( + "", "A23.A23", PO_ORG_FULL, "TRK", 10, -10, 1, + "FB", 80, 3200, 0, enterValueInCorrectRangeFromZeroMsg + ) +val invalidBlockSizeParams = InvalidAllocate( + "", "A23.A23", PO_ORG_FULL, "TRK", 10, 0, 1, + "FB", 80, -1, 0, enterValueInCorrectRangeFromZeroMsg + ) +val invalidAverageBlockLengthParams = InvalidAllocate( + "", "A23.A23", PO_ORG_FULL, "TRK", 10, 0, 1, + "FB", 80, 3200, -1, enterValueInCorrectRangeFromZeroMsg + ) + +val invalidAllocateScenarios = mapOf( + Pair("invalidDatasetNameParams", invalidDatasetNameParams), + Pair("invalidPrimaryAllocationParams", invalidPrimaryAllocationParams), + Pair("invalidDirectoryParams", invalidDirectoryParams), + Pair("invalidRecordLengthParams", invalidRecordLengthParams), + Pair("invalidSecondaryAllocationParams", invalidSecondaryAllocationParams), + Pair("invalidBlockSizeParams", invalidBlockSizeParams), + Pair("invalidAverageBlockLengthParams", invalidAverageBlockLengthParams), +) + +//rename members constants + +const val TOO_LONG_MEMBER_NAME = "123456789" +const val INVALID_NAME_VIA_CONTEXT_MENU = "@*" +const val INVALID_INVALID_FIRST_SYMBOL = "**" +const val EMPTY_STRING = "" + + +//rename dataset name +const val DATA_SET_RENAME_FAILED = "Data set rename failed" + + +val incorrectRenameMember = mapOf( + Pair(TOO_LONG_MEMBER_NAME, MEMBER_NAME_LENGTH_MESSAGE), + Pair(INVALID_NAME_VIA_CONTEXT_MENU, INVALID_MEMBER_NAME_MESSAGE), + Pair(INVALID_INVALID_FIRST_SYMBOL, INVALID_MEMBER_NAME_BEGINNING_MESSAGE), + Pair(EMPTY_STRING, MEMBER_EMPTY_NAME_MESSAGE), +) + +//member's property +const val NO_MEMBERS = "{\"items\":[],\"returnedRows\":0,\"totalRows\":0,\"moreRows\":null,\"JSONversion\":1}" + +//dialog names +const val RENAME_MEMBER_NAME = "Rename Member" +const val RENAME_DATASET_NAME = "Rename Dataset" +const val DELETION_OF_DS_MASK = "Deletion of DS Mask %s" +const val DELETION_OF_USS_PATH_ROOT = "Deletion of Uss Path Root %s" + + +//RC STRINGS + +const val RC_8 = "8.0" +const val RC_8_TEXT = "EDC5051I An error occurred when renaming a file." + + +//ws names +const val WS_NAME_WS_1 = "WS1" +const val WS_NAME_WS_2 = "WS2" +const val WS_NAME_WS_3 = "WS3" +const val WS_NAME_WS_4 = "WS4" +const val WS_NAME_WS_5 = "WS5" +const val WS_NAME_WS_6 = "WS6" +const val WS_NAME_WS_7 = "WS7" +const val WS_NAME_WS_8 = "WS8" +const val WS_NAME_WS_9 = "WS9" +const val WS_NAME_WS_10 = "WS10" + + +//members content +const val EMPTY_MEMBER_CONTENT = "" +const val SHORT_MEMBER_CONTENT = "content" + +//members name +const val MEMBER_NAME_PATTERN = "MEMBER" +const val MEMBER_NAME_1 = "MEMBER"+"1" +const val MEMBER_NAME_2 = "MEMBER"+"2" + +//mask types: +const val ZOS_MASK = "z/OS" +const val USS_MASK = "USS" + + +//masks/mask types combo + +val zosUserDatasetMask = "$ZOS_USERID.*".uppercase() +val zosUserDatasetMaskDoubleStar = "$ZOS_USERID.**".uppercase() +const val ussMask = "/u/$ZOS_USERID" +const val defaultNewUssMask = "/etc/ssh" +val singleMask = Pair("$ZOS_USERID.*", "z/OS") +val singleUssMask = Pair(ussMask, "USS") +val validZOSMasks = listOf( + "$ZOS_USERID.*", "$ZOS_USERID.**", "$ZOS_USERID.@#%", "$ZOS_USERID.@#%.*", "Q.*", "WWW.*", maskWithLength44, + ZOS_USERID +) +val validUSSMasks = listOf("/u", "/etc/ssh", ussMask) + +//todo add mask *.* when bug is fixed +val maskMessageMap = mapOf( + "1$ZOS_USERID.*" to ENTER_VALID_DS_MASK_MESSAGE, + "$ZOS_USERID.{!" to ENTER_VALID_DS_MASK_MESSAGE, + "$ZOS_USERID.A23456789.*" to QUALIFIER_ONE_TO_EIGHT, + "$ZOS_USERID." to ENTER_VALID_DS_MASK_MESSAGE, + maskWithLength45 to "Dataset mask length must not exceed 44 characters", + "$ZOS_USERID.***" to "Invalid asterisks in the qualifier" +) + +//ports +const val PORT_104431 = "104431" +const val PORT_104431_AND_1 = "1044311" + + +//dialogs +const val EDIT_WORKING_SET = "Edit Working Set" +const val CREATE_MASK_DIALOG = "Create Mask" +const val ADD_WORKING_SET_DIALOG = "Add Working Set" +const val RENAME_DATASET_MASK_DIALOG = "Edit Mask" +const val ALLOCATE_DATASET_DIALOG = "Allocate Dataset" diff --git a/src/uiTest/kotlin/Locators.kt b/src/uiTest/kotlin/Locators.kt new file mode 100644 index 000000000..d3b87bd3c --- /dev/null +++ b/src/uiTest/kotlin/Locators.kt @@ -0,0 +1,35 @@ +/* + * This program and the accompanying materials are made available under the terms of the + * Eclipse Public License v2.0 which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-v20.html + * + * SPDX-License-Identifier: EPL-2.0 + * + * Copyright IBA Group 2020 + */ +package workingset + +import com.intellij.remoterobot.search.locators.byXpath + +//common dialogs locators +val myDialogXpathLoc = byXpath("//div[@class='MyDialog']") +val removeButtonLoc = byXpath("//div[contains(@myvisibleactions, 'Down')]//div[@myaction.key='button.text.remove']") +val removeEMaskButtonLoc = byXpath("//div[contains(@myaction.key, 'dom.action.remove')]") +val linkLoc = byXpath("//div[@class='LinkLabel']") +val closeNotificationLoc = byXpath("//div[@tooltiptext.key='tooltip.close.notification']") + +//allocate dialog locators +val datasetNameInputLoc = byXpath("//div[@class='JBTextField']") +val datasetOrgDropDownLoc = byXpath("//div[@class='ComboBox']") +val inputFieldLoc = byXpath("//div[@class='JBTextField']") +val dropdownsLoc = byXpath("//div[@class='ComboBox']") +val messageLoc = byXpath("//div[@class='HeavyWeightWindow']") +val helpLoc = byXpath("//div[@class='HeavyWeightWindow'][.//div[@class='Header']]") +val errorDetailHeaderLoc = byXpath("//div[@javaclass='javax.swing.JEditorPane']") +val errorDetailBodyLoc = byXpath("//div[contains(@visible_text, 'Code:')]") +val closeDialogLoc = byXpath("//div[@class='InplaceButton']") + +// property tab +val dataTabLoc = byXpath("//div[@text='Data']") + +val treesLoc = byXpath("//div[@class='DnDAwareTree']") diff --git a/src/uiTest/kotlin/auxiliary/FirstTime.kt b/src/uiTest/kotlin/auxiliary/FirstTime.kt index 6d5f688c7..3f0f29c52 100644 --- a/src/uiTest/kotlin/auxiliary/FirstTime.kt +++ b/src/uiTest/kotlin/auxiliary/FirstTime.kt @@ -16,6 +16,7 @@ import org.junit.jupiter.api.extension.ExtendWith import auxiliary.containers.* import com.intellij.remoterobot.search.locators.Locator import org.junit.jupiter.api.Tag +import workingset.PROJECT_NAME /** * When adding UI tests to GitHub Actions pipeline, there is a need to first run dummy test, which @@ -25,15 +26,14 @@ import org.junit.jupiter.api.Tag @ExtendWith(RemoteRobotExtension::class) class ConnectionManager { private var stack = mutableListOf() - private val projectName = "untitled" @Test fun firstTime(remoteRobot: RemoteRobot) = with(remoteRobot) { welcomeFrame { - open(projectName) + open() } Thread.sleep(60000) - ideFrameImpl(projectName, stack) { + ideFrameImpl(PROJECT_NAME, stack) { dialog("For Mainframe Plugin Privacy Policy and Terms and Conditions") { clickButton("I Agree") } diff --git a/src/uiTest/kotlin/auxiliary/SmokeTest.kt b/src/uiTest/kotlin/auxiliary/SmokeTest.kt index 484620823..fd1685254 100644 --- a/src/uiTest/kotlin/auxiliary/SmokeTest.kt +++ b/src/uiTest/kotlin/auxiliary/SmokeTest.kt @@ -18,6 +18,7 @@ import org.junit.jupiter.api.extension.ExtendWith import auxiliary.containers.* import com.intellij.remoterobot.search.locators.Locator import org.junit.jupiter.api.* +import workingset.PROJECT_NAME /** * When adding UI tests to GitHub Actions pipeline, there is a need to first run dummy test, which @@ -27,7 +28,6 @@ import org.junit.jupiter.api.* @TestInstance(TestInstance.Lifecycle.PER_CLASS) @ExtendWith(RemoteRobotExtension::class) class SmokeTest { - private val projectName = "untitled" private val wsName = "ws1" private val jwsName = "jws1" private val connectionName = "conName" @@ -43,7 +43,7 @@ class SmokeTest { */ @BeforeAll fun setUpAll(remoteRobot: RemoteRobot) { - setUpTestEnvironment(projectName, fixtureStack, closableFixtureCollector, remoteRobot) + setUpTestEnvironment(fixtureStack, closableFixtureCollector, remoteRobot) } /** @@ -51,17 +51,17 @@ class SmokeTest { */ @AfterAll fun tearDownAll(remoteRobot: RemoteRobot) = with(remoteRobot) { - clearEnvironment(projectName, fixtureStack, closableFixtureCollector, remoteRobot) - ideFrameImpl(projectName, fixtureStack) { + clearEnvironment(fixtureStack, closableFixtureCollector, remoteRobot) + ideFrameImpl(PROJECT_NAME, fixtureStack) { close() } } @Test fun testBasics(remoteRobot: RemoteRobot) = with(remoteRobot) { - createConnection(projectName, fixtureStack, closableFixtureCollector, connectionName, true, remoteRobot) - createWsAndMask(projectName, wsName, listOf(defaultZosMask, defaultUssMask), connectionName, fixtureStack, closableFixtureCollector, remoteRobot) - ideFrameImpl(projectName, fixtureStack) { + createConnection(fixtureStack, closableFixtureCollector, connectionName, true, remoteRobot) + createWsAndMask(wsName, listOf(defaultZosMask, defaultUssMask), connectionName, fixtureStack, closableFixtureCollector, remoteRobot) + ideFrameImpl(PROJECT_NAME, fixtureStack) { createJesWorkingSetFromActionButton(closableFixtureCollector, fixtureStack) addJesWorkingSetDialog(fixtureStack) { addJesWorkingSet(jwsName, connectionName, ZOS_USERID, defaultJobFilter) @@ -70,8 +70,8 @@ class SmokeTest { } closableFixtureCollector.closeOnceIfExists(AddJesWorkingSetDialog.name) } - openOrCloseWorkingSetInExplorer(wsName, projectName, fixtureStack, remoteRobot) - openMaskInExplorer(defaultUssMask.first, "", projectName, fixtureStack, remoteRobot) - openMaskInExplorer(defaultZosMask.first, "", projectName, fixtureStack, remoteRobot) + openOrCloseWorkingSetInExplorer(wsName, fixtureStack, remoteRobot) + openMaskInExplorer(defaultUssMask.first, "", fixtureStack, remoteRobot) + openMaskInExplorer(defaultZosMask.first, "",fixtureStack, remoteRobot) } } diff --git a/src/uiTest/kotlin/auxiliary/components/dialogs/AddWorkingSetSubDialog.kt b/src/uiTest/kotlin/auxiliary/components/dialogs/AddWorkingSetSubDialog.kt new file mode 100644 index 000000000..ee3f41d92 --- /dev/null +++ b/src/uiTest/kotlin/auxiliary/components/dialogs/AddWorkingSetSubDialog.kt @@ -0,0 +1,68 @@ +/* + * This program and the accompanying materials are made available under the terms of the + * Eclipse Public License v2.0 which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-v20.html + * + * SPDX-License-Identifier: EPL-2.0 + * + * Copyright IBA Group 2020 + */ +package workingset.auxiliary.components.dialogs + +import auxiliary.containers.addWorkingSetDialog +import auxiliary.containers.ideFrameImpl +import com.intellij.remoterobot.RemoteRobot +import com.intellij.remoterobot.search.locators.Locator +import workingset.* +import workingset.auxiliary.components.elements.ButtonElement + +class AddWorkingSetSubDialog(fixtureStack: MutableList, remoteRobot: RemoteRobot) :AbstractDialog(fixtureStack, remoteRobot) { + + override val dialogTitle: String = ADD_WORKING_SET_DIALOG + var okButton = ButtonElement() + var cancelButton = ButtonElement() + + init { + okButton = ButtonElement(OK_TEXT, fixtureStack, remoteRobot) + cancelButton = ButtonElement(CANCEL_TEXT, fixtureStack, remoteRobot) + } + + constructor() : this(mutableListOf(), RemoteRobot(REMOTE_ROBOT_URL)){} + /* + fill all fields in add working set dialog, mask - Pair + */ + fun fillAddWorkingSet(connectionName:String, wsName:String, mask: Pair, fixtureStack: MutableList, remoteRobot: RemoteRobot) = with(remoteRobot) { + waitTitle() + ideFrameImpl(PROJECT_NAME, fixtureStack) { + addWorkingSetDialog(fixtureStack) { + addWorkingSet(wsName, connectionName, mask) + } + } + } + + /* + fill all fields in add working set dialog, mask - String + */ + fun fillAddWorkingSet(connectionName:String, wsName:String, fixtureStack: MutableList, remoteRobot: RemoteRobot) = with(remoteRobot) { + waitTitle() + ideFrameImpl(PROJECT_NAME, fixtureStack) { + + addWorkingSetDialog(fixtureStack) { + addWorkingSet(wsName, connectionName) + } + } + } + + /* + fill all fields in add working set dialog, mask - ArrayList> + */ + fun fillAddWorkingSet(connectionName:String, wsName:String, mask: ArrayList>, fixtureStack: MutableList, remoteRobot: RemoteRobot) = with(remoteRobot) { + waitTitle() + ideFrameImpl(PROJECT_NAME, fixtureStack) { + + addWorkingSetDialog(fixtureStack) { + addWorkingSet(wsName, connectionName, mask) + } + } + } +} \ No newline at end of file diff --git a/src/uiTest/kotlin/auxiliary/components/dialogs/CreateMaskSubDialog.kt b/src/uiTest/kotlin/auxiliary/components/dialogs/CreateMaskSubDialog.kt new file mode 100644 index 000000000..385b39525 --- /dev/null +++ b/src/uiTest/kotlin/auxiliary/components/dialogs/CreateMaskSubDialog.kt @@ -0,0 +1,31 @@ +/* + * This program and the accompanying materials are made available under the terms of the + * Eclipse Public License v2.0 which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-v20.html + * + * SPDX-License-Identifier: EPL-2.0 + * + * Copyright IBA Group 2020 + */ +package workingset.auxiliary.components.dialogs + +import auxiliary.containers.createMaskDialog +import auxiliary.containers.ideFrameImpl +import com.intellij.remoterobot.RemoteRobot +import com.intellij.remoterobot.search.locators.Locator +import workingset.CREATE_MASK_DIALOG +import workingset.PROJECT_NAME +import workingset.REMOTE_ROBOT_URL + +class CreateMaskSubDialog(fixtureStack: MutableList, remoteRobot: RemoteRobot) :AbstractDialog(fixtureStack, remoteRobot) { + override val dialogTitle: String = CREATE_MASK_DIALOG + constructor() : this(mutableListOf(), RemoteRobot(REMOTE_ROBOT_URL)) + + fun setMask(mask: Pair) = with(remoteRobot) { + ideFrameImpl(PROJECT_NAME, fixtureStack) { + createMaskDialog(fixtureStack) { + createMask(mask) + } + } + } +} \ No newline at end of file diff --git a/src/uiTest/kotlin/auxiliary/components/dialogs/DeletionOfDSMask.kt b/src/uiTest/kotlin/auxiliary/components/dialogs/DeletionOfDSMask.kt new file mode 100644 index 000000000..903a6f289 --- /dev/null +++ b/src/uiTest/kotlin/auxiliary/components/dialogs/DeletionOfDSMask.kt @@ -0,0 +1,29 @@ +/* + * This program and the accompanying materials are made available under the terms of the + * Eclipse Public License v2.0 which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-v20.html + * + * SPDX-License-Identifier: EPL-2.0 + * + * Copyright IBA Group 2020 + */ +package workingset.auxiliary.components.dialogs + +import com.intellij.remoterobot.RemoteRobot +import com.intellij.remoterobot.search.locators.Locator +import workingset.DELETION_OF_DS_MASK +import workingset.REMOTE_ROBOT_URL +import workingset.YES_TEXT +import workingset.auxiliary.components.elements.ButtonElement + +class DeletionOfDSMask (fixtureStack: MutableList, remoteRobot: RemoteRobot) :AbstractDialog(fixtureStack, remoteRobot) { + + override var dialogTitle: String = DELETION_OF_DS_MASK + + var yes_button = ButtonElement() + + constructor() : this(mutableListOf(), RemoteRobot(REMOTE_ROBOT_URL)){ + yes_button = ButtonElement(YES_TEXT, fixtureStack, remoteRobot) + } + +} \ No newline at end of file diff --git a/src/uiTest/kotlin/auxiliary/components/dialogs/DeletionOfUssPathRoot.kt b/src/uiTest/kotlin/auxiliary/components/dialogs/DeletionOfUssPathRoot.kt new file mode 100644 index 000000000..a0b0cd51d --- /dev/null +++ b/src/uiTest/kotlin/auxiliary/components/dialogs/DeletionOfUssPathRoot.kt @@ -0,0 +1,23 @@ +/* + * This program and the accompanying materials are made available under the terms of the + * Eclipse Public License v2.0 which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-v20.html + * + * SPDX-License-Identifier: EPL-2.0 + * + * Copyright IBA Group 2020 + */ +package workingset.auxiliary.components.dialogs + +import com.intellij.remoterobot.RemoteRobot +import com.intellij.remoterobot.search.locators.Locator +import workingset.DELETION_OF_USS_PATH_ROOT +import workingset.REMOTE_ROBOT_URL + +class DeletionOfUssPathRoot (fixtureStack: MutableList, remoteRobot: RemoteRobot) :AbstractDialog(fixtureStack, remoteRobot) { + + override var dialogTitle: String = DELETION_OF_USS_PATH_ROOT + + constructor() : this(mutableListOf(), RemoteRobot(REMOTE_ROBOT_URL)) + +} \ No newline at end of file diff --git a/src/uiTest/kotlin/auxiliary/components/dialogs/Dialog.kt b/src/uiTest/kotlin/auxiliary/components/dialogs/Dialog.kt new file mode 100644 index 000000000..b69e754e4 --- /dev/null +++ b/src/uiTest/kotlin/auxiliary/components/dialogs/Dialog.kt @@ -0,0 +1,62 @@ +/* + * This program and the accompanying materials are made available under the terms of the + * Eclipse Public License v2.0 which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-v20.html + * + * SPDX-License-Identifier: EPL-2.0 + * + * Copyright IBA Group 2020 + */ +package workingset.auxiliary.components.dialogs + +import auxiliary.RemoteRobotExtension +import auxiliary.clickButton +import auxiliary.containers.dialog +import auxiliary.containers.ideFrameImpl +import com.intellij.remoterobot.RemoteRobot +import com.intellij.remoterobot.fixtures.HeavyWeightWindowFixture +import com.intellij.remoterobot.fixtures.JTextFieldFixture +import com.intellij.remoterobot.search.locators.Locator +import org.junit.jupiter.api.TestInstance +import org.junit.jupiter.api.extension.ExtendWith +import workingset.* +import workingset.auxiliary.components.elements.ButtonElement +import java.time.Duration + +@TestInstance(TestInstance.Lifecycle.PER_CLASS) +@ExtendWith(RemoteRobotExtension::class) +abstract class AbstractDialog(internal var fixtureStack: MutableList, internal var remoteRobot: RemoteRobot){ + + abstract val dialogTitle: String + + constructor() : this(mutableListOf(), RemoteRobot(REMOTE_ROBOT_URL)) + + internal fun isShown(): Boolean = with(remoteRobot) { + var status = false + ideFrameImpl(PROJECT_NAME, fixtureStack) { + status = dialog(dialogTitle).isShowing + } + return status + } + + internal fun fillFirstFilld(fild_text: String) = with(remoteRobot) { + ideFrameImpl(PROJECT_NAME, fixtureStack) { + dialog(dialogTitle) { + find(datasetNameInputLoc).text = fild_text + } + } + } + + internal fun clickButtonByName(button_name: String) = with(remoteRobot) { + ideFrameImpl(PROJECT_NAME, fixtureStack) { + dialog(dialogTitle) { + ButtonElement(button_name, fixtureStack, remoteRobot).click() + } + } + } + + internal fun waitTitle(duration: Long=3) = with(remoteRobot){ + find(myDialogXpathLoc, Duration.ofSeconds(duration)).findText(dialogTitle) + } + +} \ No newline at end of file diff --git a/src/uiTest/kotlin/auxiliary/components/dialogs/EditWorkingSetSubDialog.kt b/src/uiTest/kotlin/auxiliary/components/dialogs/EditWorkingSetSubDialog.kt new file mode 100644 index 000000000..412dc2e72 --- /dev/null +++ b/src/uiTest/kotlin/auxiliary/components/dialogs/EditWorkingSetSubDialog.kt @@ -0,0 +1,33 @@ +/* + * This program and the accompanying materials are made available under the terms of the + * Eclipse Public License v2.0 which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-v20.html + * + * SPDX-License-Identifier: EPL-2.0 + * + * Copyright IBA Group 2020 + */ +package workingset.auxiliary.components.dialogs + +import auxiliary.containers.editWorkingSetDialog +import auxiliary.containers.ideFrameImpl +import com.intellij.remoterobot.RemoteRobot +import com.intellij.remoterobot.search.locators.Locator +import workingset.EDIT_WORKING_SET +import workingset.PROJECT_NAME +import workingset.REMOTE_ROBOT_URL + +class EditWorkingSetSubDialog(fixtureStack: MutableList, remoteRobot: RemoteRobot) :AbstractDialog(fixtureStack, remoteRobot) { + + override val dialogTitle: String = EDIT_WORKING_SET + + constructor() : this(mutableListOf(), RemoteRobot(REMOTE_ROBOT_URL)) + + fun renameWorkingSet(alreadyExistsWorkingSetName: String) = with(remoteRobot) { + ideFrameImpl(PROJECT_NAME, fixtureStack) { + editWorkingSetDialog(fixtureStack) { + renameWorkingSet(alreadyExistsWorkingSetName) + } + } + } +} diff --git a/src/uiTest/kotlin/auxiliary/components/dialogs/RenameDatasetMaskDialog.kt b/src/uiTest/kotlin/auxiliary/components/dialogs/RenameDatasetMaskDialog.kt new file mode 100644 index 000000000..0481e9afe --- /dev/null +++ b/src/uiTest/kotlin/auxiliary/components/dialogs/RenameDatasetMaskDialog.kt @@ -0,0 +1,40 @@ +/* + * This program and the accompanying materials are made available under the terms of the + * Eclipse Public License v2.0 which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-v20.html + * + * SPDX-License-Identifier: EPL-2.0 + * + * Copyright IBA Group 2020 + */ +package workingset.auxiliary.components.dialogs + +import auxiliary.containers.dialog +import auxiliary.containers.editWorkingSetDialog +import auxiliary.containers.ideFrameImpl +import com.intellij.remoterobot.RemoteRobot +import com.intellij.remoterobot.fixtures.HeavyWeightWindowFixture +import com.intellij.remoterobot.fixtures.JTextFieldFixture +import com.intellij.remoterobot.search.locators.Locator +import com.intellij.remoterobot.search.locators.byXpath +import io.kotest.matchers.string.shouldContain +import org.junit.jupiter.api.Assertions +import workingset.* +import java.time.Duration + +class RenameDatasetMaskDialog(fixtureStack: MutableList, remoteRobot: RemoteRobot) :AbstractDialog(fixtureStack, remoteRobot) { + + override val dialogTitle: String = RENAME_DATASET_MASK_DIALOG + + constructor() : this(mutableListOf(), RemoteRobot(REMOTE_ROBOT_URL)) + + fun renameMaskFromContextMenu( + fieldText: String, + remoteRobot: RemoteRobot + ) = + with(remoteRobot) { + fillFirstFilld(fieldText) + + } +} + diff --git a/src/uiTest/kotlin/auxiliary/components/elements/ButtonElement.kt b/src/uiTest/kotlin/auxiliary/components/elements/ButtonElement.kt new file mode 100644 index 000000000..e661b6f41 --- /dev/null +++ b/src/uiTest/kotlin/auxiliary/components/elements/ButtonElement.kt @@ -0,0 +1,46 @@ +/* + * This program and the accompanying materials are made available under the terms of the + * Eclipse Public License v2.0 which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-v20.html + * + * SPDX-License-Identifier: EPL-2.0 + * + * Copyright IBA Group 2020 + */ +package workingset.auxiliary.components.elements + +import auxiliary.RemoteRobotExtension +import auxiliary.containers.ideFrameImpl +import com.intellij.remoterobot.RemoteRobot +import com.intellij.remoterobot.search.locators.Locator +import com.intellij.remoterobot.utils.waitFor +import org.junit.jupiter.api.TestInstance +import org.junit.jupiter.api.extension.ExtendWith +import workingset.PROJECT_NAME +import workingset.REMOTE_ROBOT_URL +import java.time.Duration + +@TestInstance(TestInstance.Lifecycle.PER_CLASS) +@ExtendWith(RemoteRobotExtension::class) +open class ButtonElement(val buttonText: String, val fixtureStack: MutableList, val remoteRobot: RemoteRobot){ + constructor() : this("", mutableListOf(), RemoteRobot(REMOTE_ROBOT_URL)) + + internal fun isEnabled(): Boolean = with(remoteRobot) { + var status = false + ideFrameImpl(PROJECT_NAME, fixtureStack) { + status = button(buttonText).isEnabled() + } + return status + } + + internal fun click(waitTime: Long = 60) = with(remoteRobot){ + ideFrameImpl(PROJECT_NAME, fixtureStack) { + val button = button(buttonText) + waitFor(Duration.ofSeconds(waitTime)) { + button.isEnabled() + } + button.click() + } + + } +} \ No newline at end of file diff --git a/src/uiTest/kotlin/auxiliary/containers/AddWorkingSetDialog.kt b/src/uiTest/kotlin/auxiliary/containers/AddWorkingSetDialog.kt index fc0c7e773..72d6688f0 100644 --- a/src/uiTest/kotlin/auxiliary/containers/AddWorkingSetDialog.kt +++ b/src/uiTest/kotlin/auxiliary/containers/AddWorkingSetDialog.kt @@ -19,6 +19,7 @@ import com.intellij.remoterobot.fixtures.* import com.intellij.remoterobot.search.locators.Locator import com.intellij.remoterobot.search.locators.byXpath import com.intellij.remoterobot.utils.keyboard +import workingset.removeEMaskButtonLoc import java.awt.event.KeyEvent import java.time.Duration @@ -82,7 +83,7 @@ open class AddWorkingSetDialog( findText("${maskName.substring(0, 46)}...").moveMouse() } findText(maskName).click() - clickActionButton(byXpath("//div[contains(@myvisibleactions, 'Down')]//div[@myaction.key='button.text.remove']")) + clickActionButton(removeEMaskButtonLoc) } /** diff --git a/src/uiTest/kotlin/auxiliary/containers/AllocateDatasetDialog.kt b/src/uiTest/kotlin/auxiliary/containers/AllocateDatasetDialog.kt index ef4bd2d24..d4f9156d0 100644 --- a/src/uiTest/kotlin/auxiliary/containers/AllocateDatasetDialog.kt +++ b/src/uiTest/kotlin/auxiliary/containers/AllocateDatasetDialog.kt @@ -17,6 +17,7 @@ import com.intellij.remoterobot.data.RemoteComponent import com.intellij.remoterobot.fixtures.* import com.intellij.remoterobot.search.locators.Locator import com.intellij.remoterobot.search.locators.byXpath +import workingset.* import java.time.Duration /** @@ -43,18 +44,18 @@ open class AllocateDatasetDialog( blockSize: Int, averageBlockLength: Int = 0 ) { - findAll(byXpath("//div[@class='JBTextField']"))[0].text = datasetName - findAll(byXpath("//div[@class='ComboBox']"))[1].selectItem(datasetOrganization) + findAll(datasetNameInputLoc)[0].text = datasetName + findAll(datasetOrgDropDownLoc)[1].selectItem(datasetOrganization) - val datasetTextParams = findAll(byXpath("//div[@class='JBTextField']")) - val datasetComboBoxParams = findAll(byXpath("//div[@class='ComboBox']")) + val datasetTextParams = findAll(inputFieldLoc) + val datasetComboBoxParams = findAll(dropdownsLoc) datasetComboBoxParams[2].selectItem(allocationUnit) datasetTextParams[1].text = primaryAllocation.toString() datasetTextParams[2].text = secondaryAllocation.toString() datasetComboBoxParams[3].selectItem(recordFormat) - if (datasetOrganization == "Sequential (PS)") { + if (datasetOrganization == SEQUENTIAL_ORG_FULL) { datasetTextParams[3].text = recordLength.toString() datasetTextParams[4].text = blockSize.toString() datasetTextParams[5].text = averageBlockLength.toString() diff --git a/src/uiTest/kotlin/auxiliary/containers/EditWorkingSetDialog.kt b/src/uiTest/kotlin/auxiliary/containers/EditWorkingSetDialog.kt index 7d895136d..2d288bab4 100644 --- a/src/uiTest/kotlin/auxiliary/containers/EditWorkingSetDialog.kt +++ b/src/uiTest/kotlin/auxiliary/containers/EditWorkingSetDialog.kt @@ -17,6 +17,7 @@ import com.intellij.remoterobot.fixtures.ContainerFixture import com.intellij.remoterobot.fixtures.JTextFieldFixture import com.intellij.remoterobot.search.locators.Locator import com.intellij.remoterobot.search.locators.byXpath +import workingset.dropdownsLoc import java.time.Duration /** @@ -40,7 +41,7 @@ class EditWorkingSetDialog( */ fun changeConnection(newConnectionName: String) { if (newConnectionName.isEmpty().not()) { - find(byXpath("//div[@class='ComboBox']")).selectItem(newConnectionName) + find(dropdownsLoc).selectItem(newConnectionName) } } diff --git a/src/uiTest/kotlin/auxiliary/containers/IdeFrameImpl.kt b/src/uiTest/kotlin/auxiliary/containers/IdeFrameImpl.kt index fdc5a2cbf..6e43b54bf 100644 --- a/src/uiTest/kotlin/auxiliary/containers/IdeFrameImpl.kt +++ b/src/uiTest/kotlin/auxiliary/containers/IdeFrameImpl.kt @@ -19,6 +19,7 @@ import com.intellij.remoterobot.data.RemoteComponent import com.intellij.remoterobot.fixtures.FixtureName import com.intellij.remoterobot.search.locators.Locator import com.intellij.remoterobot.search.locators.byXpath +import workingset.PROJECT_NAME import java.time.Duration /** @@ -55,7 +56,7 @@ class IdeFrameImpl(remoteRobot: RemoteRobot, remoteComponent: RemoteComponent) : * Finds the Ide Frame with a specific name and modifies the fixtureStack. The frame needs to be called from the * RemoteRobot as there is no ContainerFixture containing it. */ -fun RemoteRobot.ideFrameImpl(name: String, +fun RemoteRobot.ideFrameImpl(name: String = PROJECT_NAME, fixtureStack: MutableList, function: IdeFrameImpl.() -> Unit) { find(IdeFrameImpl.xPath(name), Duration.ofSeconds(60)).apply { diff --git a/src/uiTest/kotlin/auxiliary/containers/MemberPropertiesDialog.kt b/src/uiTest/kotlin/auxiliary/containers/MemberPropertiesDialog.kt index 2cec71053..f5253d987 100644 --- a/src/uiTest/kotlin/auxiliary/containers/MemberPropertiesDialog.kt +++ b/src/uiTest/kotlin/auxiliary/containers/MemberPropertiesDialog.kt @@ -18,6 +18,8 @@ import com.intellij.remoterobot.data.RemoteComponent import com.intellij.remoterobot.fixtures.* import com.intellij.remoterobot.search.locators.Locator import com.intellij.remoterobot.search.locators.byXpath +import workingset.dataTabLoc +import workingset.inputFieldLoc import java.time.Duration /** @@ -64,22 +66,27 @@ open class MemberPropertiesDialog( curRecNum: String, begRecNum: String, changedRecNum: String): Boolean { - var result = true - val memberGeneralParams = findAll(byXpath("//div[@class='JBTextField']")) + + val memberGeneralParams = findAll(inputFieldLoc) + if(memberGeneralParams[0].text != memName || memberGeneralParams[1].text != version || memberGeneralParams[2].text != createDate || memberGeneralParams[3].text != modDate || memberGeneralParams[4].text != modTime || memberGeneralParams[5].text != userId){ - result = false + return false } - Thread.sleep(1000) - find(byXpath("//div[@text='Data']")).click() - Thread.sleep(1000) - val memberDataParams = findAll(byXpath("//div[@class='JBTextField']")) - if(memberDataParams[0].text != curRecNum || memberDataParams[1].text != begRecNum || memberDataParams[2].text != changedRecNum){ - result = false + + find(dataTabLoc).click() + + val memberDataParams = findAll(inputFieldLoc) + + if(memberDataParams[0].text != curRecNum || + memberDataParams[1].text != begRecNum || + memberDataParams[2].text != changedRecNum) + { + return false } - return result + return true } /** diff --git a/src/uiTest/kotlin/auxiliary/containers/WelcomeFrame.kt b/src/uiTest/kotlin/auxiliary/containers/WelcomeFrame.kt index cd6fac535..ec6e3901a 100644 --- a/src/uiTest/kotlin/auxiliary/containers/WelcomeFrame.kt +++ b/src/uiTest/kotlin/auxiliary/containers/WelcomeFrame.kt @@ -17,6 +17,7 @@ import com.intellij.remoterobot.fixtures.CommonContainerFixture import com.intellij.remoterobot.fixtures.DefaultXpath import com.intellij.remoterobot.fixtures.FixtureName import com.intellij.remoterobot.search.locators.byXpath +import workingset.PROJECT_NAME import java.time.Duration /** @@ -37,12 +38,12 @@ class WelcomeFrame(remoteRobot: RemoteRobot, remoteComponent: RemoteComponent) : /** * Opens project with projectName, which is located in the resources of the uiTest source set. */ - fun open(projectName: String) { + fun open() { openProject.click() Thread.sleep(1000) dialog("Open File or Project") { textField(byXpath("//div[@class='BorderlessTextField']")).text = - System.getProperty("user.dir") + "/src/uiTest/resources/$projectName" + System.getProperty("user.dir") + "/src/uiTest/resources/$PROJECT_NAME" Thread.sleep(1000) clickButton("OK") } diff --git a/src/uiTest/kotlin/auxiliary/utils.kt b/src/uiTest/kotlin/auxiliary/utils.kt index 52bed6ee1..1e21c4fe8 100644 --- a/src/uiTest/kotlin/auxiliary/utils.kt +++ b/src/uiTest/kotlin/auxiliary/utils.kt @@ -33,6 +33,8 @@ import okhttp3.tls.HandshakeCertificates import okhttp3.tls.HeldCertificate import org.junit.jupiter.api.TestInfo import testutils.MockResponseDispatcher +import workingset.* +//import workingset.testutils.InjectDispatcher import java.awt.event.KeyEvent import java.net.InetAddress import java.time.Duration @@ -40,80 +42,64 @@ import java.util.concurrent.TimeUnit lateinit var mockServer: MockWebServer lateinit var responseDispatcher: MockResponseDispatcher +//lateinit var injectDispatcher: InjectDispatcher enum class JobStatus { ACTIVE, INPUT, OUTPUT } //Change ZOS_USERID, ZOS_PWD, CONNECTION_URL with valid values before UI tests execution -const val ZOS_USERID = "user" +const val ZOS_USERID = "User" const val ZOS_PWD = "changeme" const val CONNECTION_URL = "changeme" const val ENTER_VALID_DS_MASK_MESSAGE = "Enter valid dataset mask" -const val IDENTICAL_MASKS_MESSAGE = "You cannot add several identical masks to table" -const val EMPTY_DATASET_MESSAGE = "You are going to create a Working Set that doesn't fetch anything" + + const val TEXT_FIELD_LENGTH_MESSAGE = "Text field must not exceed 8 characters." const val MEMBER_NAME_LENGTH_MESSAGE = "Member name must not exceed 8 characters." const val FILE_NAME_LENGTH_MESSAGE = "Filename must not exceed 255 characters." -const val MEMBER_EMPTY_NAME_MESSAGE = "This field must not be blank" -const val INVALID_MEMBER_NAME_MESSAGE = "Member name should contain only A-Z a-z 0-9 or national characters" -const val INVALID_MEMBER_NAME_BEGINNING_MESSAGE = "Member name should start with A-Z a-z or national characters" const val JOB_ID_LENGTH_MESSAGE = "Job ID length must be 8 characters." const val TEXT_FIELD_CONTAIN_MESSAGE = "Text field should contain only A-Z, a-z, 0-9, *, %." const val JOBID_CONTAIN_MESSAGE = "Text field should contain only A-Z, a-z, 0-9" const val FILE_RESRVED_SYMBOL_MESSAGE = "Filename must not contain reserved '/' symbol." const val PREFIX_OWNER_JOBID_MESSAGE = "You must provide either an owner and a prefix or a job ID." const val IDENTICAL_FILTERS_MESSAGE = "You cannot add several identical job filters to table" -const val DATASET_NAME_LENGTH_MESSAGE = "Dataset name cannot exceed 44 characters" -const val DATASET_INVALID_SECTION_MESSAGE = - "Each name segment (qualifier) is 1 to 8 characters, the first of which must be alphabetic (A to Z) or national (# @ \$). The remaining seven characters are either alphabetic, numeric (0 - 9), national, a hyphen (-). Name segments are separated by a period (.)" - +const val QUALIFIER_ONE_TO_EIGHT = "Qualifier must be in 1 to 8 characters" val maskWithLength44 = - "$ZOS_USERID." + "A2345678.".repeat((44 - (ZOS_USERID.length + 1)) / 9) + "A".repeat((44 - (ZOS_USERID.length + 1)) % 9) + ZOS_USERID + ".A2345678".repeat((44 - (ZOS_USERID.length + 1)) / 9) + "A".repeat((44 - (ZOS_USERID.length + 1)) % 9) val maskWithLength45 = - "$ZOS_USERID." + "A2345678.".repeat((45 - (ZOS_USERID.length + 1)) / 9) + "A".repeat((45 - (ZOS_USERID.length + 1)) % 9) - -//todo add mask *.* when bug is fixed -val maskMessageMap = mapOf( - "1$ZOS_USERID.*" to ENTER_VALID_DS_MASK_MESSAGE, - "$ZOS_USERID.{!" to ENTER_VALID_DS_MASK_MESSAGE, - "$ZOS_USERID.A23456789.*" to "Qualifier must be in 1 to 8 characters", - "$ZOS_USERID." to ENTER_VALID_DS_MASK_MESSAGE, - maskWithLength45 to "Dataset mask length must not exceed 44 characters", - "$ZOS_USERID.***" to "Invalid asterisks in the qualifier" -) + "$ZOS_USERID." + "A2345678.".repeat((45 - (ZOS_USERID.length + 1)) / 9) + "A".repeat((45 - (ZOS_USERID.length + 1)) % 9) + + + + -val validZOSMasks = listOf( - "$ZOS_USERID.*", "$ZOS_USERID.**", "$ZOS_USERID.@#%", "$ZOS_USERID.@#%.*", "Q.*", "WWW.*", maskWithLength44, - ZOS_USERID -) -val validUSSMasks = listOf("/u", "/etc/ssh", "/u/$ZOS_USERID") val validJobsFilters = listOf( - Triple("*", ZOS_USERID, ""), - Triple("TEST1", ZOS_USERID, ""), - Triple("", "", "JOB01234"), - Triple("TEST*", ZOS_USERID, ""), - Triple("TEST**", ZOS_USERID, ""), - Triple("TEST***", ZOS_USERID, ""), - Triple("TEST1", "$ZOS_USERID*", ""), - Triple("TEST1", "$ZOS_USERID**", ""), - Triple("TEST1", "$ZOS_USERID***", ""), - Triple("TEST***", "$ZOS_USERID***", "") + Triple("*", ZOS_USERID, ""), + Triple("TEST1", ZOS_USERID, ""), + Triple("", "", "JOB01234"), + Triple("TEST*", ZOS_USERID, ""), + Triple("TEST**", ZOS_USERID, ""), + Triple("TEST***", ZOS_USERID, ""), + Triple("TEST1", "$ZOS_USERID*", ""), + Triple("TEST1", "$ZOS_USERID**", ""), + Triple("TEST1", "$ZOS_USERID***", ""), + Triple("TEST***", "$ZOS_USERID***", "") ) val invalidJobsFiltersMap = mapOf( - Pair(Triple("123456789", ZOS_USERID, ""), 1) to TEXT_FIELD_LENGTH_MESSAGE, - Pair(Triple("123456789", "A23456789", ""), 1) to TEXT_FIELD_LENGTH_MESSAGE, - Pair(Triple("*", "A23456789", ""), 2) to TEXT_FIELD_LENGTH_MESSAGE, - Pair(Triple("", "", "A23456789"), 3) to JOB_ID_LENGTH_MESSAGE, - Pair(Triple("", "", "A2"), 3) to JOB_ID_LENGTH_MESSAGE, - Pair(Triple("@", ZOS_USERID, ""), 1) to TEXT_FIELD_CONTAIN_MESSAGE, - Pair(Triple("*", "@", ""), 2) to TEXT_FIELD_CONTAIN_MESSAGE, - Pair(Triple("", "", "@@@@@@@@"), 3) to JOBID_CONTAIN_MESSAGE, - Pair(Triple("*", ZOS_USERID, "JOB45678"), 1) to PREFIX_OWNER_JOBID_MESSAGE, - Pair(Triple("*", "", "JOB45678"), 1) to PREFIX_OWNER_JOBID_MESSAGE, - Pair(Triple("", ZOS_USERID, "JOB45678"), 2) to PREFIX_OWNER_JOBID_MESSAGE + Pair(Triple("123456789", ZOS_USERID, ""), 1) to TEXT_FIELD_LENGTH_MESSAGE, + Pair(Triple("123456789", "A23456789", ""), 1) to TEXT_FIELD_LENGTH_MESSAGE, + Pair(Triple("*", "A23456789", ""), 2) to TEXT_FIELD_LENGTH_MESSAGE, + Pair(Triple("", "", "A23456789"), 3) to JOB_ID_LENGTH_MESSAGE, + Pair(Triple("", "", "A2"), 3) to JOB_ID_LENGTH_MESSAGE, + Pair(Triple("@", ZOS_USERID, ""), 1) to TEXT_FIELD_CONTAIN_MESSAGE, + Pair(Triple("*", "@", ""), 2) to TEXT_FIELD_CONTAIN_MESSAGE, + Pair(Triple("", "", "@@@@@@@@"), 3) to JOBID_CONTAIN_MESSAGE, + Pair(Triple("*", ZOS_USERID, "JOB45678"), 1) to PREFIX_OWNER_JOBID_MESSAGE, + Pair(Triple("*", "", "JOB45678"), 1) to PREFIX_OWNER_JOBID_MESSAGE, + Pair(Triple("", ZOS_USERID, "JOB45678"), 2) to PREFIX_OWNER_JOBID_MESSAGE ) val viewTree = byXpath("//div[@class='JBViewport'][.//div[@class='DnDAwareTree']]") @@ -124,33 +110,33 @@ enum class UssFileType { File, Directory } * Waits 60 seconds for the button to be enabled and then clicks on it. */ fun CommonContainerFixture.clickButton(text: String) { - val button = button(text) - waitFor(Duration.ofSeconds(60)) { - button.isEnabled() - } - button.click() + val button = button(text) + waitFor(Duration.ofSeconds(60)) { + button.isEnabled() + } + button.click() } /** * Waits a specific amount of time for the button to be enabled and then clicks on it. */ fun CommonContainerFixture.clickButton(locator: Locator, duration: Duration = Duration.ofSeconds(60)) { - val button = button(locator) - waitFor(duration) { - button.isEnabled() - } - button.click() + val button = button(locator) + waitFor(duration) { + button.isEnabled() + } + button.click() } /** * Waits 60 seconds for the action button to be enabled and then clicks on it. */ fun CommonContainerFixture.clickActionButton(locator: Locator) { - val button = actionButton(locator) - waitFor(Duration.ofSeconds(60)) { - button.isEnabled() - } - button.click() + val button = actionButton(locator) + waitFor(Duration.ofSeconds(60)) { + button.isEnabled() + } + button.click() } /** @@ -158,23 +144,23 @@ fun CommonContainerFixture.clickActionButton(locator: Locator) { */ fun ContainerFixture.createWSFromContextMenu( fixtureStack: MutableList, - closableFixtureCollector: ClosableFixtureCollector + closableFixtureCollector: ClosableFixtureCollector, ) { - explorer { - fileExplorer.click() - find(viewTree).rightClick() - } - actionMenu(remoteRobot, "New").click() - - //workaround when an action menu contains more than 2 action menu items, and you need to choose the 3d item - runJs( - """ + explorer { + fileExplorer.click() + find(viewTree).rightClick() + } + actionMenu(remoteRobot, NEW_POINT_TEXT).click() + + //workaround when an action menu contains more than 2 action menu items, and you need to choose the 3d item + runJs( + """ const point = new java.awt.Point(${locationOnScreen.x}, ${locationOnScreen.y}); robot.moveMouse(component, point); """ - ) - actionMenuItem(remoteRobot, "Working Set").click() - closableFixtureCollector.add(AddWorkingSetDialog.xPath(), fixtureStack) + ) + actionMenuItem(remoteRobot, WORKING_SET).click() + closableFixtureCollector.add(AddWorkingSetDialog.xPath(), fixtureStack) } /** @@ -182,16 +168,16 @@ fun ContainerFixture.createWSFromContextMenu( */ fun ContainerFixture.editWSFromContextMenu( wsName: String, fixtureStack: MutableList, - closableFixtureCollector: ClosableFixtureCollector + closableFixtureCollector: ClosableFixtureCollector, ) { - explorer { - fileExplorer.click() - find(viewTree).findText(wsName) - .rightClick() - Thread.sleep(3000) - } - actionMenuItem(remoteRobot, "Edit").click() - closableFixtureCollector.add(EditWorkingSetDialog.xPath(), fixtureStack) + explorer { + fileExplorer.click() + find(viewTree).findText(wsName) + .rightClick() + Thread.sleep(3000) + } + actionMenuItem(remoteRobot, EDIT_POINT_TEXT).click() + closableFixtureCollector.add(EditWorkingSetDialog.xPath(), fixtureStack) } /** @@ -199,45 +185,43 @@ fun ContainerFixture.editWSFromContextMenu( */ fun ContainerFixture.createMask( wsName: String, fixtureStack: MutableList, - closableFixtureCollector: ClosableFixtureCollector + closableFixtureCollector: ClosableFixtureCollector, ) { - explorer { - fileExplorer.click() - find(viewTree).findText(wsName) - .rightClick() - Thread.sleep(3000) - } - actionMenu(remoteRobot, "New").click() - actionMenuItem(remoteRobot, "Mask").click() - closableFixtureCollector.add(CreateMaskDialog.xPath(), fixtureStack) + explorer { + fileExplorer.click() + find(viewTree).findText(wsName) + .rightClick() + } + actionMenu(remoteRobot, NEW_POINT_TEXT).click() + actionMenuItem(remoteRobot, MASK_POINT_TEXT).click() + closableFixtureCollector.add(CreateMaskDialog.xPath(), fixtureStack) } /** * Deletes a working set via context menu from explorer. */ fun ContainerFixture.deleteWSFromContextMenu(wsName: String) { - explorer { - fileExplorer.click() - find(viewTree).findText(wsName) - .rightClick() - Thread.sleep(3000) - } - actionMenuItem(remoteRobot, "Delete").click() - find(byXpath("//div[@class='MyDialog' and @title='Deletion of Working Set $wsName']")) + explorer { + fileExplorer.click() + find(viewTree).findText(wsName) + .rightClick() + Thread.sleep(3000) + } + actionMenuItem(remoteRobot, DELETE_TEXT).click() } /** * Deletes a JES working set via context menu from explorer. */ fun ContainerFixture.deleteJWSFromContextMenu(jwsName: String) { - explorer { - jesExplorer.click() - find(viewTree).findText(jwsName) - .rightClick() - Thread.sleep(3000) - } - actionMenuItem(remoteRobot, "Delete").click() - find(byXpath("//div[@class='MyDialog' and @title='Deletion of JES Working Set $jwsName']")) + explorer { + jesExplorer.click() + find(viewTree).findText(jwsName) + .rightClick() + Thread.sleep(3000) + } + actionMenuItem(remoteRobot, DELETE_TEXT).click() + find(byXpath("//div[@class='MyDialog' and @title='Deletion of JES Working Set $jwsName']")) } /** @@ -245,16 +229,16 @@ fun ContainerFixture.deleteJWSFromContextMenu(jwsName: String) { */ fun ContainerFixture.createJWSFromContextMenu( fixtureStack: MutableList, - closableFixtureCollector: ClosableFixtureCollector + closableFixtureCollector: ClosableFixtureCollector, ) { - explorer { - jesExplorer.click() - find(viewTree).rightClick() - Thread.sleep(3000) - } - actionMenu(remoteRobot, "New").click() - actionMenuItem(remoteRobot, "JES Working Set").click() - closableFixtureCollector.add(AddJesWorkingSetDialog.xPath(), fixtureStack) + explorer { + jesExplorer.click() + find(viewTree).rightClick() + Thread.sleep(3000) + } + actionMenu(remoteRobot, NEW_POINT_TEXT).click() + actionMenuItem(remoteRobot, "JES Working Set").click() + closableFixtureCollector.add(AddJesWorkingSetDialog.xPath(), fixtureStack) } /** @@ -262,17 +246,17 @@ fun ContainerFixture.createJWSFromContextMenu( */ fun ContainerFixture.createJobsFilter( jwsName: String, fixtureStack: MutableList, - closableFixtureCollector: ClosableFixtureCollector + closableFixtureCollector: ClosableFixtureCollector, ) { - explorer { - jesExplorer.click() - find(viewTree).findText(jwsName) - .rightClick() - Thread.sleep(3000) - } - actionMenu(remoteRobot, "New").click() - actionMenuItem(remoteRobot, "Jobs Filter").click() - closableFixtureCollector.add(CreateJobsFilterDialog.xPath(), fixtureStack) + explorer { + jesExplorer.click() + find(viewTree).findText(jwsName) + .rightClick() + Thread.sleep(3000) + } + actionMenu(remoteRobot, NEW_POINT_TEXT).click() + actionMenuItem(remoteRobot, "Jobs Filter").click() + closableFixtureCollector.add(CreateJobsFilterDialog.xPath(), fixtureStack) } /** @@ -280,38 +264,38 @@ fun ContainerFixture.createJobsFilter( */ fun ContainerFixture.editJWSFromContextMenu( jwsName: String, fixtureStack: MutableList, - closableFixtureCollector: ClosableFixtureCollector + closableFixtureCollector: ClosableFixtureCollector, ) { - explorer { - jesExplorer.click() - find(viewTree).findText(jwsName) - .rightClick() - Thread.sleep(3000) - } - actionMenuItem(remoteRobot, "Edit").click() - closableFixtureCollector.add(EditJesWorkingSetDialog.xPath(), fixtureStack) + explorer { + jesExplorer.click() + find(viewTree).findText(jwsName) + .rightClick() + Thread.sleep(3000) + } + actionMenuItem(remoteRobot, EDIT_POINT_TEXT).click() + closableFixtureCollector.add(EditJesWorkingSetDialog.xPath(), fixtureStack) } /** * Creates a working set via action button. */ -fun ContainerFixture.createWorkingSetFromActionButton( +fun ContainerFixture.callCreateWorkingSetFromActionButton( closableFixtureCollector: ClosableFixtureCollector, - fixtureStack: MutableList + fixtureStack: MutableList, ) { - explorer { - fileExplorer.click() - createConfigItem() - } - find( - byXpath("//div[@class='HeavyWeightWindow']"), - Duration.ofSeconds(30) - ).findAllText().forEach { - if (it.text == "Working Set") { - it.click() - closableFixtureCollector.add(AddWorkingSetDialog.xPath(), fixtureStack) + explorer { + fileExplorer.click() + createConfigItem() + } + find( + byXpath("//div[@class='HeavyWeightWindow']"), + Duration.ofSeconds(30) + ).findAllText().forEach { + if (it.text == WORKING_SET) { + it.click() + closableFixtureCollector.add(AddWorkingSetDialog.xPath(), fixtureStack) + } } - } } /** @@ -319,21 +303,21 @@ fun ContainerFixture.createWorkingSetFromActionButton( */ fun ContainerFixture.createJesWorkingSetFromActionButton( closableFixtureCollector: ClosableFixtureCollector, - fixtureStack: MutableList + fixtureStack: MutableList, ) { - explorer { - jesExplorer.click() - createConfigItem() - } - find( - byXpath("//div[@class='HeavyWeightWindow']"), - Duration.ofSeconds(30) - ).findAllText().forEach { - if (it.text == "JES Working Set") { - it.click() - closableFixtureCollector.add(AddJesWorkingSetDialog.xPath(), fixtureStack) + explorer { + jesExplorer.click() + createConfigItem() + } + find( + messageLoc, + Duration.ofSeconds(30) + ).findAllText().forEach { + if (it.text == "JES Working Set") { + it.click() + closableFixtureCollector.add(AddJesWorkingSetDialog.xPath(), fixtureStack) + } } - } } /** @@ -341,28 +325,27 @@ fun ContainerFixture.createJesWorkingSetFromActionButton( */ fun ContainerFixture.createConnectionFromActionButton( closableFixtureCollector: ClosableFixtureCollector, - fixtureStack: MutableList + fixtureStack: MutableList, ) { - explorer { - jesExplorer.click() - createConfigItem() - } - find( - byXpath("//div[@class='HeavyWeightWindow']"), - Duration.ofSeconds(30) - ).findAllText().forEach { - if (it.text == "Connection") { - it.click() - closableFixtureCollector.add(AddConnectionDialog.xPath(), fixtureStack) + explorer { + jesExplorer.click() + createConfigItem() + } + find( + messageLoc, + Duration.ofSeconds(30) + ).findAllText().forEach { + if (it.text == "Connection") { + it.click() + closableFixtureCollector.add(AddConnectionDialog.xPath(), fixtureStack) + } } - } } /** * Steps to create a connection(valid or invalid) from settings . */ fun createConnection( - projectName: String, fixtureStack: MutableList, closableFixtureCollector: ClosableFixtureCollector, connectionName: String, @@ -370,350 +353,347 @@ fun createConnection( remoteRobot: RemoteRobot, url: String = CONNECTION_URL, user: String = ZOS_USERID, - password: String = ZOS_PWD + password: String = ZOS_PWD, ) = with(remoteRobot) { - ideFrameImpl(projectName, fixtureStack) { - explorer { - fileExplorer.click() - settings(closableFixtureCollector, fixtureStack) - } - settingsDialog(fixtureStack) { - configurableEditor { - conTab.click() - add(closableFixtureCollector, fixtureStack) - } - addConnectionDialog(fixtureStack) { - if (isValidConnection) { - addConnection(connectionName, url, user, password, true) - } else { - addConnection(connectionName, "${url}1", user, password, true) + ideFrameImpl(PROJECT_NAME, fixtureStack) { + explorer { + fileExplorer.click() + settings(closableFixtureCollector, fixtureStack) } - clickButton("OK") - Thread.sleep(3000) - } - closableFixtureCollector.closeOnceIfExists(AddConnectionDialog.name) - if (isValidConnection.not()) { - errorCreatingConnectionDialog(closableFixtureCollector, fixtureStack) { - clickButton("Yes") + settingsDialog(fixtureStack) { + configurableEditor { + conTab.click() + add(closableFixtureCollector, fixtureStack) + } + addConnectionDialog(fixtureStack) { + if (isValidConnection) { + addConnection(connectionName, url, user, password, true) + } else { + addConnection(connectionName, "${url}1", user, password, true) + } + clickButton(OK_TEXT) + Thread.sleep(3000) + } + closableFixtureCollector.closeOnceIfExists(AddConnectionDialog.name) + if (isValidConnection.not()) { + errorCreatingConnectionDialog(closableFixtureCollector, fixtureStack) { + clickButton(YES_TEXT) + } + closableFixtureCollector.closeOnceIfExists(ErrorCreatingConnectionDialog.name) + } + clickButton("OK") } - closableFixtureCollector.closeOnceIfExists(ErrorCreatingConnectionDialog.name) - } - clickButton("OK") + closableFixtureCollector.closeOnceIfExists(SettingsDialog.name) } - closableFixtureCollector.closeOnceIfExists(SettingsDialog.name) - } } /** * Deletes all JES working sets, working sets and connections. To be used in BeforeAll and AfterAll tests methods. */ fun clearEnvironment( - projectName: String, fixtureStack: MutableList, closableFixtureCollector: ClosableFixtureCollector, - remoteRobot: RemoteRobot + remoteRobot: RemoteRobot, ) = with(remoteRobot) { - ideFrameImpl(projectName, fixtureStack) { - explorer { - fileExplorer.click() - settings(closableFixtureCollector, fixtureStack) - } - settingsDialog(fixtureStack) { - configurableEditor { - workingSetsTab.click() - deleteAllItems() - jesWorkingSetsTab.click() - deleteAllItems() - conTab.click() - deleteAllItems() - } - clickButton("OK") + ideFrameImpl(PROJECT_NAME, fixtureStack) { + explorer { + fileExplorer.click() + settings(closableFixtureCollector, fixtureStack) + } + settingsDialog(fixtureStack) { + configurableEditor { + workingSetsTab.click() + deleteAllItems() + jesWorkingSetsTab.click() + deleteAllItems() + conTab.click() + deleteAllItems() + } + clickButton(OK_TEXT) + } + closableFixtureCollector.closeOnceIfExists(SettingsDialog.name) } - closableFixtureCollector.closeOnceIfExists(SettingsDialog.name) - } } /** * Opens a project and an explorer, clears test environment before tests execution. */ fun setUpTestEnvironment( - projectName: String, fixtureStack: MutableList, closableFixtureCollector: ClosableFixtureCollector, - remoteRobot: RemoteRobot + remoteRobot: RemoteRobot, ) = with(remoteRobot) { - welcomeFrame { - open(projectName) - } - Thread.sleep(10000) - - ideFrameImpl(projectName, fixtureStack) { - try { - if (dialog("For Mainframe Plugin Privacy Policy and Terms and Conditions").isShowing) { - clickButton("Dismiss") - } - } catch (e: WaitForConditionTimeoutException) { - e.message.shouldContain("Failed to find 'Dialog' by 'title For Mainframe Plugin Privacy Policy and Terms and Conditions'") + welcomeFrame { + open() } - try { - find(byXpath("//div[@class='ProjectViewTree']")) - stripeButton(byXpath("//div[@accessiblename='Project' and @class='StripeButton' and @text='Project']")) - .click() - } catch (e: WaitForConditionTimeoutException) { - //do nothing if ProjectViewTree is hidden + Thread.sleep(10000) + + ideFrameImpl(PROJECT_NAME, fixtureStack) { + try { + if (dialog("For Mainframe Plugin Privacy Policy and Terms and Conditions").isShowing) { + clickButton("Dismiss") + } + } catch (e: WaitForConditionTimeoutException) { + e.message.shouldContain("Failed to find 'Dialog' by 'title For Mainframe Plugin Privacy Policy and Terms and Conditions'") + } + try { + find(byXpath("//div[@class='ProjectViewTree']")) + stripeButton(byXpath("//div[@accessiblename='Project' and @class='StripeButton' and @text='Project']")) + .click() + } catch (e: WaitForConditionTimeoutException) { + //do nothing if ProjectViewTree is hidden + } + forMainframe() } - forMainframe() - } - clearEnvironment(projectName, fixtureStack, closableFixtureCollector, remoteRobot) + clearEnvironment(fixtureStack, closableFixtureCollector, remoteRobot) } /** * Opens a mask in the working set in explorer. */ fun openWSOpenMaskInExplorer( - wsName: String, maskName: String, projectName: String, - fixtureStack: MutableList, remoteRobot: RemoteRobot + wsName: String, maskName: String, + fixtureStack: MutableList, remoteRobot: RemoteRobot, ) = - with(remoteRobot) { - ideFrameImpl(projectName, fixtureStack) { - explorer { - fileExplorer.click() - find(viewTree).findText(wsName).doubleClick() - Thread.sleep(3000) - find(viewTree).findText(maskName).doubleClick() - Thread.sleep(2000) - waitFor(Duration.ofSeconds(20)) { find(viewTree).hasText("loading…").not() } - find(viewTree).findAllText().shouldNotContain("Error") - find(viewTree).findText(wsName).doubleClick() - } + with(remoteRobot) { + ideFrameImpl(PROJECT_NAME, fixtureStack) { + explorer { + fileExplorer.click() + find(viewTree).findText(wsName).doubleClick() + Thread.sleep(3000) + find(viewTree).findText(maskName).doubleClick() + Thread.sleep(2000) + waitFor(Duration.ofSeconds(20)) { find(viewTree).hasText("loading…").not() } + find(viewTree).findAllText().shouldNotContain("Error") + find(viewTree).findText(wsName).doubleClick() + } + } } - } /** * Double-clicks on the working set to open or close it in explorer. */ fun openOrCloseWorkingSetInExplorer( - wsName: String, projectName: String, - fixtureStack: MutableList, remoteRobot: RemoteRobot + wsName: String, fixtureStack: MutableList, remoteRobot: RemoteRobot, ) = with(remoteRobot) { - ideFrameImpl(projectName, fixtureStack) { - explorer { - fileExplorer.click() - find(viewTree).findText(wsName).doubleClick() - Thread.sleep(1000) + ideFrameImpl(PROJECT_NAME, fixtureStack) { + explorer { + fileExplorer.click() + find(viewTree).findText(wsName).doubleClick() + Thread.sleep(1000) + } } - } } /** * Double-clicks on the jes working set to open or close it in explorer. */ fun openOrCloseJesWorkingSetInExplorer( - jwsName: String, projectName: String, - fixtureStack: MutableList, remoteRobot: RemoteRobot + jwsName: String, + fixtureStack: MutableList, remoteRobot: RemoteRobot, ) = with(remoteRobot) { - ideFrameImpl(projectName, fixtureStack) { - explorer { - jesExplorer.click() - find(viewTree).findText(jwsName).doubleClick() - Thread.sleep(3000) + ideFrameImpl(PROJECT_NAME, fixtureStack) { + explorer { + jesExplorer.click() + find(viewTree).findText(jwsName).doubleClick() + Thread.sleep(3000) + } } - } } /** * Double-clicks on the mask in explorer to open it and checks the message if required. */ fun openMaskInExplorer( - maskName: String, expectedError: String, projectName: String, - fixtureStack: MutableList, remoteRobot: RemoteRobot + maskName: String, expectedError: String, + fixtureStack: MutableList, remoteRobot: RemoteRobot, ) = - with(remoteRobot) { - ideFrameImpl(projectName, fixtureStack) { - explorer { - fileExplorer.click() - find(viewTree).findText(maskName).doubleClick() - Thread.sleep(20000) - var allText = "" - find(viewTree).findAllText().forEach { allText += it.text } - if (expectedError.isEmpty().not()) { - allText.shouldContain(expectedError) - } else { - allText.shouldNotContain("Error") + with(remoteRobot) { + ideFrameImpl(PROJECT_NAME, fixtureStack) { + explorer { + fileExplorer.click() + find(viewTree).findText(maskName).doubleClick() + Thread.sleep(20000) + var allText = "" + find(viewTree).findAllText().forEach { allText += it.text } + if (expectedError.isEmpty().not()) { + allText.shouldContain(expectedError) + } else { + allText.shouldNotContain("Error") + } + } } - } } - } /** * Double-clicks on the job filter in explorer to open it and checks the message if required. */ fun openJobFilterInExplorer( - filter: Triple, expectedError: String, projectName: String, - fixtureStack: MutableList, remoteRobot: RemoteRobot + filter: Triple, expectedError: String, + fixtureStack: MutableList, remoteRobot: RemoteRobot, ) = with(remoteRobot) { - val textToFind = if (filter.third == "") { - "PREFIX=${filter.first} OWNER=${filter.second}".uppercase() - } else { - "JobID=${filter.third}" - } - ideFrameImpl(projectName, fixtureStack) { - explorer { - jesExplorer.click() - find(viewTree).findText(textToFind).doubleClick() - Thread.sleep(2000) - waitFor(Duration.ofSeconds(20)) { find(viewTree).hasText("loading…").not() } - if (expectedError.isEmpty().not()) { - find(viewTree).findText(expectedError) - } else { - find(viewTree).findAllText().shouldNotContain("Error") - } + val textToFind = if (filter.third == "") { + "PREFIX=${filter.first} OWNER=${filter.second}".uppercase() + } else { + "JobID=${filter.third}" + } + ideFrameImpl(PROJECT_NAME, fixtureStack) { + explorer { + jesExplorer.click() + find(viewTree).findText(textToFind).doubleClick() + Thread.sleep(2000) + waitFor(Duration.ofSeconds(20)) { find(viewTree).hasText("loading…").not() } + if (expectedError.isEmpty().not()) { + find(viewTree).findText(expectedError) + } else { + find(viewTree).findAllText().shouldNotContain("Error") + } + } } - } } /** * Double-clicks on job filter to close it in explorer. */ fun closeFilterInExplorer( - filter: Triple, projectName: String, - fixtureStack: MutableList, remoteRobot: RemoteRobot + filter: Triple, + fixtureStack: MutableList, remoteRobot: RemoteRobot, ) = - with(remoteRobot) { - val textToFind = if (filter.third == "") { - "PREFIX=${filter.first} OWNER=${filter.second}".uppercase() - } else { - "JobID=${filter.third}" - } - ideFrameImpl(projectName, fixtureStack) { - explorer { - jesExplorer.click() - find(viewTree).findText(textToFind).doubleClick() - } + with(remoteRobot) { + val textToFind = if (filter.third == "") { + "PREFIX=${filter.first} OWNER=${filter.second}".uppercase() + } else { + "JobID=${filter.third}" + } + ideFrameImpl(PROJECT_NAME, fixtureStack) { + explorer { + jesExplorer.click() + find(viewTree).findText(textToFind).doubleClick() + } + } } - } /** * Double-clicks on mask to close it in explorer. */ fun closeMaskInExplorer( - maskName: String, projectName: String, - fixtureStack: MutableList, remoteRobot: RemoteRobot + maskName: String, + fixtureStack: MutableList, remoteRobot: RemoteRobot, ) = - with(remoteRobot) { - ideFrameImpl(projectName, fixtureStack) { - explorer { - fileExplorer.click() - find(viewTree).findText(maskName).doubleClick() - } + with(remoteRobot) { + ideFrameImpl(PROJECT_NAME, fixtureStack) { + explorer { + fileExplorer.click() + find(viewTree).findText(maskName).doubleClick() + } + } } - } /** * Checks that the mask or the working set is not displayed in explorer. */ fun checkItemWasDeletedWSRefreshed( - deletedItem: String, projectName: String, - fixtureStack: MutableList, remoteRobot: RemoteRobot + deletedItem: String, + fixtureStack: MutableList, remoteRobot: RemoteRobot, ) = with(remoteRobot) { - ideFrameImpl(projectName, fixtureStack) { - explorer { - shouldThrow { - find(viewTree).findText(deletedItem) - } + ideFrameImpl(PROJECT_NAME, fixtureStack) { + explorer { + shouldThrow { + find(viewTree).findText(deletedItem) + } + } } - } } /** * Checks that the job filter is not displayed in explorer. */ fun checkFilterWasDeletedJWSRefreshed( - deletedFilter: Triple, projectName: String, - fixtureStack: MutableList, remoteRobot: RemoteRobot + deletedFilter: Triple, + fixtureStack: MutableList, remoteRobot: RemoteRobot, ) = with(remoteRobot) { - val textToFind = if (deletedFilter.third == "") { - "PREFIX=${deletedFilter.first} OWNER=${deletedFilter.second}" - } else { - "JobID=${deletedFilter.third}" - } - ideFrameImpl(projectName, fixtureStack) { - explorer { - jesExplorer.click() - shouldThrow { - find(viewTree).findText(textToFind) - } + val textToFind = if (deletedFilter.third == "") { + "PREFIX=${deletedFilter.first} OWNER=${deletedFilter.second}" + } else { + "JobID=${deletedFilter.third}" + } + ideFrameImpl(PROJECT_NAME, fixtureStack) { + explorer { + jesExplorer.click() + shouldThrow { + find(viewTree).findText(textToFind) + } + } } - } } /** * Deletes dataset via context menu. */ fun deleteDataset( - datasetName: String, projectName: String, - fixtureStack: MutableList, remoteRobot: RemoteRobot + datasetName: String, + fixtureStack: MutableList, remoteRobot: RemoteRobot, ) = with(remoteRobot) { - ideFrameImpl(projectName, fixtureStack) { - explorer { - fileExplorer.click() - find(viewTree).findAllText(datasetName).last().rightClick() - } - actionMenuItem(remoteRobot, "Delete").click() - dialog("Confirm Files Deletion") { - clickButton("Yes") + ideFrameImpl(PROJECT_NAME, fixtureStack) { + explorer { + fileExplorer.click() + find(viewTree).findAllText(datasetName).last().rightClick() + } + actionMenuItem(remoteRobot, "Delete").click() + dialog("Confirm Files Deletion") { + clickButton(YES_TEXT) + } +// Thread.sleep(3000) } - Thread.sleep(3000) - } } /** * Opens the file and copies it's content. */ fun openLocalFileAndCopyContent( - filePath: String, projectName: String, - fixtureStack: MutableList, remoteRobot: RemoteRobot + filePath: String, + fixtureStack: MutableList, remoteRobot: RemoteRobot, ) = with(remoteRobot) { - ideFrameImpl(projectName, fixtureStack) { - actionMenu(remoteRobot, "File").click() - runJs( - """ + ideFrameImpl(PROJECT_NAME, fixtureStack) { + actionMenu(remoteRobot, "File").click() + runJs( + """ const point = new java.awt.Point(${locationOnScreen.x}, ${locationOnScreen.y}); robot.moveMouse(component, point); """ - ) - actionMenuItem(remoteRobot, "Open...").click() - Thread.sleep(3000) - dialog("Open File or Project") { - textField(byXpath("//div[@class='BorderlessTextField']")).text = - filePath - Thread.sleep(5000) - clickButton("OK") - } - with(textEditor()) { - keyboard { - hotKey(KeyEvent.VK_CONTROL, KeyEvent.VK_A) - hotKey(KeyEvent.VK_CONTROL, KeyEvent.VK_C) - hotKey(KeyEvent.VK_CONTROL, KeyEvent.VK_F4) - } + ) + actionMenuItem(remoteRobot, "Open...").click() + Thread.sleep(3000) + dialog("Open File or Project") { + textField(byXpath("//div[@class='BorderlessTextField']")).text = + filePath + Thread.sleep(5000) + clickButton("OK") + } + with(textEditor()) { + keyboard { + hotKey(KeyEvent.VK_CONTROL, KeyEvent.VK_A) + hotKey(KeyEvent.VK_CONTROL, KeyEvent.VK_C) + hotKey(KeyEvent.VK_CONTROL, KeyEvent.VK_F4) + } + } } - } } /** * Submits the job via context menu. */ fun submitJob( - jobName: String, projectName: String, - fixtureStack: MutableList, remoteRobot: RemoteRobot + jobName: String, + fixtureStack: MutableList, remoteRobot: RemoteRobot, ) = with(remoteRobot) { - ideFrameImpl(projectName, fixtureStack) { - explorer { - fileExplorer.click() - find(viewTree).findText(jobName).rightClick() + ideFrameImpl(PROJECT_NAME, fixtureStack) { + explorer { + fileExplorer.click() + find(viewTree).findText(jobName).rightClick() + } + actionMenuItem(remoteRobot, "Submit Job").click() } - actionMenuItem(remoteRobot, "Submit Job").click() - } } /** @@ -721,37 +701,37 @@ fun submitJob( */ fun createMemberAndPasteContent( datasetName: String, - memberName: String, projectName: String, + memberName: String, fixtureStack: MutableList, - remoteRobot: RemoteRobot + remoteRobot: RemoteRobot, ) = - with(remoteRobot) { - ideFrameImpl(projectName, fixtureStack) { - explorer { - fileExplorer.click() - find(viewTree).findAllText(datasetName).last().rightClick() - } - actionMenu(remoteRobot, "New").click() - actionMenuItem(remoteRobot, "Member").click() - dialog("Create Member") { - find(byXpath("//div[@class='JBTextField']")).text = memberName - } - clickButton("OK") - Thread.sleep(5000) - explorer { - find(viewTree).findAllText(memberName).last().doubleClick() - } - with(textEditor()) { - keyboard { - hotKey(KeyEvent.VK_CONTROL, KeyEvent.VK_V) - Thread.sleep(2000) - hotKey(KeyEvent.VK_CONTROL, KeyEvent.VK_SHIFT, KeyEvent.VK_S) - Thread.sleep(2000) - hotKey(KeyEvent.VK_CONTROL, KeyEvent.VK_F4) + with(remoteRobot) { + ideFrameImpl(PROJECT_NAME, fixtureStack) { + explorer { + fileExplorer.click() + find(viewTree).findAllText(datasetName).last().rightClick() + } + actionMenu(remoteRobot, NEW_POINT_TEXT).click() + actionMenuItem(remoteRobot, "Member").click() + dialog("Create Member") { + find(datasetNameInputLoc).text = memberName + } + clickButton(OK_TEXT) + Thread.sleep(5000) + explorer { + find(viewTree).findAllText(memberName).last().doubleClick() + } + with(textEditor()) { + keyboard { + hotKey(KeyEvent.VK_CONTROL, KeyEvent.VK_V) + Thread.sleep(2000) + hotKey(KeyEvent.VK_CONTROL, KeyEvent.VK_SHIFT, KeyEvent.VK_S) + Thread.sleep(2000) + hotKey(KeyEvent.VK_CONTROL, KeyEvent.VK_F4) + } + } } - } } - } /** * Allocates a PDS dataset and creates a mask for it. @@ -760,81 +740,93 @@ fun createMemberAndPasteContent( fun allocatePDSAndCreateMask( wsName: String, datasetName: String, - projectName: String, fixtureStack: MutableList, closableFixtureCollector: ClosableFixtureCollector, remoteRobot: RemoteRobot, maskName: String? = null, directory: Int = 1, - openWs: Boolean = true + openWs: Boolean = true, ) = with(remoteRobot) { - ideFrameImpl(projectName, fixtureStack) { - if (maskName != null) { - createMask(wsName, fixtureStack, closableFixtureCollector) - createMaskDialog(fixtureStack) { - createMask(Pair(maskName, "z/OS")) - clickButton("OK") - } - } + ideFrameImpl(PROJECT_NAME, fixtureStack) { + if (maskName != null) { + createMask(wsName, fixtureStack, closableFixtureCollector) + createMaskDialog(fixtureStack) { + createMask(Pair(maskName, ZOS_MASK)) + clickButton(OK_TEXT) + } + } - explorer { - fileExplorer.click() - find(viewTree).findText(wsName).rightClick() - } - actionMenu(remoteRobot, "New").click() - actionMenuItem(remoteRobot, "Dataset").click() - allocateDatasetDialog(fixtureStack) { - allocateDataset(datasetName, "PO", "TRK", 10, 1, directory, "VB", 255, 6120) - clickButton("OK") - Thread.sleep(500) - } + explorer { + fileExplorer.click() + find(viewTree).findText(wsName).rightClick() + } + actionMenu(remoteRobot, NEW_POINT_TEXT).click() + actionMenuItem(remoteRobot, DATASET_POINT_TEXT).click() + allocateDatasetDialog(fixtureStack) { + allocateDataset(datasetName, PO_ORG_FULL, "TRK", 10, 1, directory, "VB", 255, 6120) + clickButton(OK_TEXT) + Thread.sleep(500) + } - val textToFind = "Dataset ${datasetName.uppercase()} Has Been Created" - val dialog = find(byXpath("//div[@class='MyDialog']")) - val dialogContents = dialog.findAllText().map(RemoteText::text).joinToString("") - val hasText = dialogContents.contains(textToFind) - if (!hasText) { - throw Exception("Text is not found in dialog: $textToFind") +// val textToFind = "Dataset ${datasetName.uppercase()} Has Been Created" +// val dialog = find(byXpath("//div[@class='MyDialog']")) +// val dialogContents = dialog.findAllText().map(RemoteText::text).joinToString("") +// val hasText = dialogContents.contains(textToFind) +// if (!hasText) { +// throw Exception("Text is not found in dialog: $textToFind") +// } + +// if (maskName != null) { +// clickButton("No") +// Thread.sleep(200) +// } else { +// clickButton("Yes") +// Thread.sleep(200) +// } } - - if (maskName != null) { - clickButton("No") - Thread.sleep(200) - } else { - clickButton("Yes") - Thread.sleep(200) + if (openWs) { + openOrCloseWorkingSetInExplorer(wsName, fixtureStack, remoteRobot) } - } - if (openWs) { - openOrCloseWorkingSetInExplorer(wsName, projectName, fixtureStack, remoteRobot) - } } /** * Creates working set without masks. */ fun createWsWithoutMask( - projectName: String, wsName: String, connectionName: String, fixtureStack: MutableList, closableFixtureCollector: ClosableFixtureCollector, - remoteRobot: RemoteRobot + remoteRobot: RemoteRobot, ) = with(remoteRobot) { - ideFrameImpl(projectName, fixtureStack) { - createWSFromContextMenu(fixtureStack, closableFixtureCollector) - addWorkingSetDialog(fixtureStack) { - addWorkingSet(wsName, connectionName) - clickButton("OK") - Thread.sleep(3000) - find(byXpath("//div[@class='HeavyWeightWindow']")).findText( - EMPTY_DATASET_MESSAGE - ) - clickButton("OK") - Thread.sleep(3000) + ideFrameImpl(PROJECT_NAME, fixtureStack) { + createWSFromContextMenu(fixtureStack, closableFixtureCollector) + addWorkingSetDialog(fixtureStack) { + addWorkingSet(wsName, connectionName) + clickButton(OK_TEXT) + } + closableFixtureCollector.closeOnceIfExists(AddWorkingSetDialog.name) + } +} + +/** + * Creates working set without masks from action button. + */ +fun createWsWithoutMaskActionButton( + wsName: String, + connectionName: String, + fixtureStack: MutableList, + closableFixtureCollector: ClosableFixtureCollector, + remoteRobot: RemoteRobot, +) = with(remoteRobot) { + ideFrameImpl(PROJECT_NAME, fixtureStack) { + callCreateWorkingSetFromActionButton(closableFixtureCollector, fixtureStack) + addWorkingSetDialog(fixtureStack) { + addWorkingSet(wsName, connectionName) + clickButton(OK_TEXT) + } + closableFixtureCollector.closeOnceIfExists(AddWorkingSetDialog.name) } - closableFixtureCollector.closeOnceIfExists(AddWorkingSetDialog.name) - } } /** @@ -843,57 +835,69 @@ fun createWsWithoutMask( fun allocateMemberForPDS( datasetName: String, memberName: String, - projectName: String, fixtureStack: MutableList, - remoteRobot: RemoteRobot + remoteRobot: RemoteRobot, ) = with(remoteRobot) { - ideFrameImpl(projectName, fixtureStack) { - explorer { - fileExplorer.click() - find(viewTree).findText(datasetName).rightClick() - } + ideFrameImpl(PROJECT_NAME, fixtureStack) { + explorer { + fileExplorer.click() + find(viewTree).findText(datasetName).rightClick() + } + + actionMenu(remoteRobot, "New").click() + actionMenuItem(remoteRobot, "Member").click() + createMemberDialog(fixtureStack) { + createMember(memberName) + clickButton("OK") +// Thread.sleep(5000) - actionMenu(remoteRobot, "New").click() - actionMenuItem(remoteRobot, "Member").click() - createMemberDialog(fixtureStack) { - createMember(memberName) - clickButton("OK") - Thread.sleep(5000) + } } - } } +fun closeNotificztion(fixtureStack: MutableList, remoteRobot: RemoteRobot) = with(remoteRobot){ + ideFrameImpl(PROJECT_NAME, fixtureStack) { + find(closeNotificationLoc).click() + } +} /** * Allocates a sequential dataset. */ fun allocateDataSet( wsName: String, datasetName: String, - projectName: String, fixtureStack: MutableList, - remoteRobot: RemoteRobot + remoteRobot: RemoteRobot, ) = with(remoteRobot) { - ideFrameImpl(projectName, fixtureStack) { - explorer { - fileExplorer.click() - find(viewTree).findText(wsName).rightClick() - } - actionMenu(remoteRobot, "New").click() - actionMenuItem(remoteRobot, "Dataset").click() - allocateDatasetDialog(fixtureStack) { - allocateDataset(datasetName, "PS", "TRK", 10, 1, 0, "VB", 255, 6120) - clickButton("OK") - Thread.sleep(10000) - } - find(byXpath("//div[@class='MyDialog']")).findText("Dataset $datasetName Has Been Created") - clickButton("No") - explorer { - fileExplorer.click() - find(viewTree).findText(wsName).rightClick() + ideFrameImpl(PROJECT_NAME, fixtureStack) { + explorer { + fileExplorer.click() + find(viewTree).findText(wsName).rightClick() + } + actionMenu(remoteRobot, "New").click() + actionMenuItem(remoteRobot, "Dataset").click() + allocateDatasetDialog(fixtureStack) { + allocateDataset(datasetName, POE_ORG_FULL, "TRK", 10, 1, 1, "VB", 255, 6120) + clickButton(OK_TEXT) + Thread.sleep(3000) + } +// try { +// find(myDialogXpathLoc).findText("Dataset $datasetName Has Been ") +// } +// catch (e: NoSuchElementException) { +// find(myDialogXpathLoc).findText("Dataset $datasetName Has Been Created") +// } +// finally{ +// clickButton(NO_TEXT)} +// find(myDialogXpathLoc).findText("Dataset $datasetName Has Been Created") +// clickButton(NO_TEXT) + explorer { + fileExplorer.click() + find(viewTree).findText(wsName).rightClick() + } + actionMenuItem(remoteRobot, REFRESH_POINT_TEXT).click() +// Thread.sleep(3000) } - actionMenuItem(remoteRobot, "Refresh").click() - Thread.sleep(3000) - } } /** @@ -903,40 +907,39 @@ fun checkErrorNotification( errorHeader: String, errorType: String, errorDetail: String, - projectName: String, fixtureStack: MutableList, - remoteRobot: RemoteRobot + remoteRobot: RemoteRobot, ) = with(remoteRobot) { - ideFrameImpl(projectName, fixtureStack) { - var errorMessage = "" - try { - find(byXpath("//div[@class='LinkLabel']")).click() - } catch (e: WaitForConditionTimeoutException) { - e.message.shouldContain("Failed to find 'ComponentFixture' by '//div[@class='LinkLabel']'") - } - find(byXpath("//div[@javaclass='javax.swing.JLabel']")).findText(errorHeader) - find(byXpath("//div[@class='JEditorPane']")).findAllText().forEach { - errorMessage += it.text - } - find(byXpath("//div[@tooltiptext.key='tooltip.close.notification']")).click() - if (!(errorMessage.contains(errorType) && errorMessage.contains(errorDetail))) { - throw Exception("Error message is different from expected") + ideFrameImpl(PROJECT_NAME, fixtureStack) { + var errorMessage = "" + try { + find(linkLoc).click() + } catch (e: WaitForConditionTimeoutException) { + e.message.shouldContain("Failed to find 'ComponentFixture' by '//div[@class='LinkLabel']'") + } + find(errorDetailHeaderLoc).findText(errorHeader) + find(errorDetailBodyLoc).findAllText().forEach { + errorMessage += it.text + } + find(closeDialogLoc).click() + find(closeDialogLoc).click() + if (!(errorMessage.contains(errorType) && errorMessage.contains(errorDetail))) { + throw Exception("Error message is different from expected") + } } - } } /** * Creates uss file or directory for provided mask name */ fun createUssFile( - ussMaskName: String, - fileName: String, - fileType: UssFileType, - projectName: String, - fixtureStack: MutableList, - remoteRobot: RemoteRobot + ussMaskName: String, + fileName: String, + fileType: UssFileType, + fixtureStack: MutableList, + remoteRobot: RemoteRobot, ) = with(remoteRobot) { - ideFrameImpl(projectName, fixtureStack) { + ideFrameImpl(PROJECT_NAME, fixtureStack) { explorer { fileExplorer.click() find(viewTree).findText(ussMaskName).rightClick() @@ -951,6 +954,22 @@ fun createUssFile( } } +fun buildFinalListDatasetJson(mapListDatasets: MutableMap): String { + var result = "{}" + if (mapListDatasets.isNotEmpty()) { + var listDatasetsJson = "{\"items\":[" + mapListDatasets.forEach { + listDatasetsJson += it.value + } + result = listDatasetsJson.dropLast(1) + "],\n" + + " \"returnedRows\": ${mapListDatasets.size},\n" + + " \"totalRows\": ${mapListDatasets.size},\n" + + " \"JSONversion\": 1\n" + + "}" + } + return result +} + /** * Builds json list of objects based on provided map */ @@ -986,177 +1005,204 @@ fun buildResponseListJson( * Starts mock server for UI tests. */ fun startMockServer() { - val localhost = InetAddress.getByName("localhost").canonicalHostName - val localhostCertificate = HeldCertificate.Builder() - .addSubjectAlternativeName(localhost) - .duration(10, TimeUnit.MINUTES) - .build() - val serverCertificates = HandshakeCertificates.Builder() - .heldCertificate(localhostCertificate) - .build() - mockServer = MockWebServer() - responseDispatcher = MockResponseDispatcher() - mockServer.dispatcher = responseDispatcher - mockServer.useHttps(serverCertificates.sslSocketFactory(), false) - mockServer.start() + val localhost = InetAddress.getByName("localhost").canonicalHostName + val localhostCertificate = HeldCertificate.Builder() + .addSubjectAlternativeName(localhost) + .duration(10, TimeUnit.MINUTES) + .build() + val serverCertificates = HandshakeCertificates.Builder() + .heldCertificate(localhostCertificate) + .build() + mockServer = MockWebServer() + responseDispatcher = MockResponseDispatcher() +// injectDispatcher = InjectDispatcher() + mockServer.dispatcher = responseDispatcher + mockServer.useHttps(serverCertificates.sslSocketFactory(), false) + mockServer.start() } /** * Creates working set and a mask. */ fun createWsAndMask( - projectName: String, wsName: String, masks: List>, connectionName: String, fixtureStack: MutableList, closableFixtureCollector: ClosableFixtureCollector, - remoteRobot: RemoteRobot + remoteRobot: RemoteRobot, ) = with(remoteRobot) { - createWsWithoutMask(projectName, wsName, connectionName, fixtureStack, closableFixtureCollector, remoteRobot) - ideFrameImpl(projectName, fixtureStack) { - masks.forEach { mask -> - createMask(wsName, fixtureStack, closableFixtureCollector) - createMaskDialog(fixtureStack) { - createMask(mask) - Thread.sleep(3000) - clickButton("OK") - } - closableFixtureCollector.closeOnceIfExists(CreateMaskDialog.name) + createWsWithoutMaskActionButton(wsName, connectionName, fixtureStack, closableFixtureCollector, remoteRobot) + ideFrameImpl(PROJECT_NAME, fixtureStack) { + masks.forEach { mask -> + createMask(wsName, fixtureStack, closableFixtureCollector) + createMaskDialog(fixtureStack) { + createMask(mask) +// Thread.sleep(3000) + clickButton(OK_TEXT) + } + closableFixtureCollector.closeOnceIfExists(CreateMaskDialog.name) + } } - } } /** * Creates json to list dataset. */ fun listDS(dsName: String, dsNtp: String, dsOrg: String): String { - return "{\n" + - " \"dsname\": \"${dsName}\",\n" + - " \"blksz\": \"3200\",\n" + - " \"catnm\": \"TEST.CATALOG.MASTER\",\n" + - " \"cdate\": \"2021/11/15\",\n" + - " \"dev\": \"3390\",\n" + - " \"dsntp\": \"${dsNtp}\",\n" + - " \"dsorg\": \"${dsOrg}\",\n" + - " \"edate\": \"***None***\",\n" + - " \"extx\": \"1\",\n" + - " \"lrecl\": \"255\",\n" + - " \"migr\": \"NO\",\n" + - " \"mvol\": \"N\",\n" + - " \"ovf\": \"NO\",\n" + - " \"rdate\": \"2021/11/17\",\n" + - " \"recfm\": \"VB\",\n" + - " \"sizex\": \"10\",\n" + - " \"spacu\": \"TRACKS\",\n" + - " \"used\": \"1\",\n" + - " \"vol\": \"TESTVOL\",\n" + - " \"vols\": \"TESTVOL\"\n" + - " }," + return "{\n" + + " \"dsname\": \"${dsName}\",\n" + + " \"blksz\": \"3200\",\n" + + " \"catnm\": \"TEST.CATALOG.MASTER\",\n" + + " \"cdate\": \"2021/11/15\",\n" + + " \"dev\": \"3390\",\n" + + " \"dsntp\": \"${dsNtp}\",\n" + + " \"dsorg\": \"${dsOrg}\",\n" + + " \"edate\": \"***None***\",\n" + + " \"extx\": \"1\",\n" + + " \"lrecl\": \"255\",\n" + + " \"migr\": \"NO\",\n" + + " \"mvol\": \"N\",\n" + + " \"ovf\": \"NO\",\n" + + " \"rdate\": \"2021/11/17\",\n" + + " \"recfm\": \"VB\",\n" + + " \"sizex\": \"10\",\n" + + " \"spacu\": \"TRACKS\",\n" + + " \"used\": \"1\",\n" + + " \"vol\": \"TESTVOL\",\n" + + " \"vols\": \"TESTVOL\"\n" + + " }," } /** * Creates valid connection to mock server. */ fun createValidConnectionWithMock( - testInfo: TestInfo, connectionName: String, projectName: String, fixtureStack: MutableList, - closableFixtureCollector: ClosableFixtureCollector, remoteRobot: RemoteRobot + testInfo: TestInfo, connectionName: String, fixtureStack: MutableList, + closableFixtureCollector: ClosableFixtureCollector, remoteRobot: RemoteRobot, ) = with(remoteRobot) { - responseDispatcher.injectEndpoint( - "${testInfo.displayName}_info", - { it?.requestLine?.contains("zosmf/info") ?: false }, - { MockResponse().setBody(responseDispatcher.readMockJson("infoResponse") ?: "") } - ) - responseDispatcher.injectEndpoint( - "${testInfo.displayName}_resttopology", - { it?.requestLine?.contains("zosmf/resttopology/systems") ?: false }, - { MockResponse().setBody(responseDispatcher.readMockJson("infoResponse") ?: "") } - ) - createConnection( - projectName, - fixtureStack, - closableFixtureCollector, - connectionName, - true, - remoteRobot, - "https://${mockServer.hostName}:${mockServer.port}" - ) + responseDispatcher.injectEndpoint( + "${testInfo.displayName}_info", + { it?.requestLine?.contains("zosmf/info") ?: false }, + { MockResponse().setBody(responseDispatcher.readMockJson("infoResponse") ?: "") } + ) + responseDispatcher.injectEndpoint( + "${testInfo.displayName}_resttopology", + { it?.requestLine?.contains("zosmf/resttopology/systems") ?: false }, + { MockResponse().setBody(responseDispatcher.readMockJson("infoResponse") ?: "") } + ) + createConnection( + fixtureStack, + closableFixtureCollector, + connectionName, + true, + remoteRobot, + "https://${mockServer.hostName}:${mockServer.port}" + ) } fun createEmptyDatasetMember( datasetName: String, memberName: String, - projectName: String, fixtureStack: MutableList, - remoteRobot: RemoteRobot + fixtureStack: MutableList, + remoteRobot: RemoteRobot, ) = - with(remoteRobot) { - ideFrameImpl(projectName, fixtureStack) { - explorer { - fileExplorer.click() - find(viewTree).findAllText(datasetName).last().rightClick() - } - actionMenu(remoteRobot, "New").click() - actionMenuItem(remoteRobot, "Member").click() - dialog("Create Member") { - find(byXpath("//div[@class='JBTextField']")).text = memberName - } - clickButton("OK") + with(remoteRobot) { + ideFrameImpl(PROJECT_NAME, fixtureStack) { + explorer { + fileExplorer.click() + find(viewTree).findAllText(datasetName).last().rightClick() + } + actionMenu(remoteRobot, "New").click() + actionMenuItem(remoteRobot, "Member").click() + dialog("Create Member") { + find(byXpath("//div[@class='JBTextField']")).text = memberName + } + clickButton("OK") + } } - } /** * Pastes content to dataset member from buffer. */ fun pasteContent( memberName: String, - projectName: String, fixtureStack: MutableList, - remoteRobot: RemoteRobot + fixtureStack: MutableList, + remoteRobot: RemoteRobot, ) = - with(remoteRobot) { - ideFrameImpl(projectName, fixtureStack) { - explorer { - find(viewTree).findAllText(memberName).last().doubleClick() - Thread.sleep(2000) - } - with(textEditor()) { - keyboard { - hotKey(KeyEvent.VK_CONTROL, KeyEvent.VK_V) - Thread.sleep(2000) - hotKey(KeyEvent.VK_CONTROL, KeyEvent.VK_SHIFT, KeyEvent.VK_S) - Thread.sleep(2000) - hotKey(KeyEvent.VK_CONTROL, KeyEvent.VK_F4) + with(remoteRobot) { + ideFrameImpl(PROJECT_NAME, fixtureStack) { + explorer { + find(viewTree).findAllText(memberName).last().doubleClick() + Thread.sleep(2000) + } + with(textEditor()) { + keyboard { + hotKey(KeyEvent.VK_CONTROL, KeyEvent.VK_V) + Thread.sleep(2000) + hotKey(KeyEvent.VK_CONTROL, KeyEvent.VK_SHIFT, KeyEvent.VK_S) + Thread.sleep(2000) + hotKey(KeyEvent.VK_CONTROL, KeyEvent.VK_F4) + } + } } - } } - } /** * Creates json for submitted job. */ fun setBodyJobSubmit(jobName: String, jobStatus: JobStatus): String { - return "{\n" + - " \"owner\": \"${ZOS_USERID.uppercase()}\",\n" + - " \"phase\": 14,\n" + - " \"subsystem\": \"JES2\",\n" + - " \"phase-name\": \"Job is actively executing\",\n" + - " \"job-correlator\": \"J0007380S0W1....DB23523B.......:\",\n" + - " \"type\": \"JOB\",\n" + - " \"url\": \"https://${mockServer.hostName}:${mockServer.port}/zosmf/restjobs/jobs/J0007380S0W1....DB23523B.......%3A\",\n" + - " \"jobid\": \"JOB07380\",\n" + - " \"class\": \"B\",\n" + - " \"files-url\": \"https://${mockServer.hostName}:${mockServer.port}/zosmf/restjobs/jobs/J0007380S0W1....DB23523B.......%3A/files\",\n" + - " \"jobname\": \"${jobName}\",\n" + - " \"status\": \"${jobStatus}\",\n" + - " \"retcode\": null\n" + - "}\n" + return "{\n" + + " \"owner\": \"${ZOS_USERID.uppercase()}\",\n" + + " \"phase\": 14,\n" + + " \"subsystem\": \"JES2\",\n" + + " \"phase-name\": \"Job is actively executing\",\n" + + " \"job-correlator\": \"J0007380S0W1....DB23523B.......:\",\n" + + " \"type\": \"JOB\",\n" + + " \"url\": \"https://${mockServer.hostName}:${mockServer.port}/zosmf/restjobs/jobs/J0007380S0W1....DB23523B.......%3A\",\n" + + " \"jobid\": \"JOB07380\",\n" + + " \"class\": \"B\",\n" + + " \"files-url\": \"https://${mockServer.hostName}:${mockServer.port}/zosmf/restjobs/jobs/J0007380S0W1....DB23523B.......%3A/files\",\n" + + " \"jobname\": \"${jobName}\",\n" + + " \"status\": \"${jobStatus}\",\n" + + " \"retcode\": null\n" + + "}\n" +} +/** + * Click by button with text. + */ +fun clickByText(buttonText: String,fixtureStack: MutableList,remoteRobot: RemoteRobot)= with(remoteRobot){ + ideFrameImpl(PROJECT_NAME, fixtureStack) { + clickButton(buttonText) + } } fun replaceInJson(fileName: String, valuesMap: Map): String { var sourceJson = responseDispatcher.readMockJson(fileName) ?: "" - valuesMap.forEach{ entry -> + valuesMap.forEach { entry -> sourceJson = sourceJson.replace(entry.key, entry.value) } return sourceJson -} \ No newline at end of file +} + +fun createMask(wsName: String, maskName: String, fixtureStack: MutableList,closableFixtureCollector: ClosableFixtureCollector, mask_type: String, remoteRobot: RemoteRobot)= with(remoteRobot){ + ideFrameImpl(PROJECT_NAME, fixtureStack) { + createMask(wsName, fixtureStack, closableFixtureCollector) + createMaskDialog(fixtureStack) { + createMask(Pair(maskName, mask_type)) + clickButton(OK_TEXT) + } + } +} + +fun buildListMembersJson(listMembersInDataset: MutableList): String { + var members = "[ " + if (listMembersInDataset.isNotEmpty()) { + listMembersInDataset.forEach { members += "{\"member\": \"${it}\"}," } + } + members = members.dropLast(1) + "]" + return "{\"items\":$members,\"returnedRows\": ${listMembersInDataset.size},\"JSONversion\": 1}" +} + diff --git a/src/uiTest/kotlin/jes/CancelHoldReleaseJobTest.kt b/src/uiTest/kotlin/jes/CancelHoldReleaseJobTest.kt index 12605c4df..00d3da8d0 100644 --- a/src/uiTest/kotlin/jes/CancelHoldReleaseJobTest.kt +++ b/src/uiTest/kotlin/jes/CancelHoldReleaseJobTest.kt @@ -22,6 +22,8 @@ import com.intellij.remoterobot.utils.keyboard import okhttp3.mockwebserver.MockResponse import org.junit.jupiter.api.* import org.junit.jupiter.api.extension.ExtendWith +import workingset.EMPTY_DATASET_MESSAGE +import workingset.PROJECT_NAME import java.awt.event.KeyEvent import java.io.File @@ -58,11 +60,10 @@ class CancelHoldReleaseJobTest { @BeforeAll fun setUpAll(testInfo: TestInfo, remoteRobot: RemoteRobot) { startMockServer() - setUpTestEnvironment(projectName, fixtureStack, closableFixtureCollector, remoteRobot) + setUpTestEnvironment(fixtureStack, closableFixtureCollector, remoteRobot) createValidConnectionWithMock( testInfo, connectionName, - projectName, fixtureStack, closableFixtureCollector, remoteRobot @@ -89,7 +90,7 @@ class CancelHoldReleaseJobTest { MockResponse().setBody(buildListMembersJson()) } ) - allocatePDSAndCreateMask(wsName, datasetName, projectName, fixtureStack, closableFixtureCollector, remoteRobot) + allocatePDSAndCreateMask(wsName, datasetName, fixtureStack, closableFixtureCollector, remoteRobot) createJob(testInfo, remoteRobot) } @@ -111,10 +112,10 @@ class CancelHoldReleaseJobTest { { it?.requestLine?.contains("DELETE /zosmf/restfiles/ds/${datasetName.uppercase()}") ?: false }, { MockResponse().setBody("{}") } ) - deleteDataset(datasetName, projectName, fixtureStack, remoteRobot) + deleteDataset(datasetName, fixtureStack, remoteRobot) mockServer.shutdown() - clearEnvironment(projectName, fixtureStack, closableFixtureCollector, remoteRobot) - ideFrameImpl(projectName, fixtureStack) { + clearEnvironment(fixtureStack, closableFixtureCollector, remoteRobot) + ideFrameImpl(PROJECT_NAME, fixtureStack) { close() } } @@ -210,7 +211,7 @@ class CancelHoldReleaseJobTest { * Closes notifications and jobs tabs in jobs panel if exists. */ private fun closeNotificationsAndJobsTabsIfExist(remoteRobot: RemoteRobot) = with(remoteRobot) { - ideFrameImpl(projectName, fixtureStack) { + ideFrameImpl(PROJECT_NAME, fixtureStack) { try { find(byXpath("//div[@javaclass='javax.swing.JLabel']")) .click() @@ -241,7 +242,7 @@ class CancelHoldReleaseJobTest { */ private fun getIdSubmittedJob(remoteRobot: RemoteRobot): String = with(remoteRobot) { var jobId = "" - ideFrameImpl(projectName, fixtureStack) { + ideFrameImpl(PROJECT_NAME, fixtureStack) { jobId = find(byXpath("//div[@class='Tree']")).findAllText()[2].text.trim() } return jobId @@ -257,7 +258,7 @@ class CancelHoldReleaseJobTest { JobAction.SUBMIT -> "CC 0000" else -> throw Exception("Unknown action") } - ideFrameImpl(projectName, fixtureStack) { + ideFrameImpl(PROJECT_NAME, fixtureStack) { find(byXpath("//div[contains(@accessiblename.key, 'editor.accessible.name')]")).findText( "JOB $jobName($jobId) EXECUTED" ) @@ -274,7 +275,7 @@ class CancelHoldReleaseJobTest { * Closes tab in jobs panel. */ private fun closeJobTabInJobsPanel(remoteRobot: RemoteRobot) = with(remoteRobot) { - ideFrameImpl(projectName, fixtureStack) { + ideFrameImpl(PROJECT_NAME, fixtureStack) { findAll(byXpath("//div[@class='TabPanel'][.//div[@text='Jobs:']]//div[@class='ContentTabLabel']")).last() .findText( "//'$datasetName($jobName)'" @@ -407,7 +408,7 @@ class CancelHoldReleaseJobTest { else -> throw Exception("Unknown action") } - ideFrameImpl(projectName, fixtureStack) { + ideFrameImpl(PROJECT_NAME, fixtureStack) { clickActionButton(byXpath("//div[@class='ActionButton' and @myaction='$myAction']")) } } @@ -426,7 +427,7 @@ class CancelHoldReleaseJobTest { JobAction.RELEASE -> "$jobName: $jobId has been released" JobAction.SUBMIT -> "Job $jobName has been submitted" } - ideFrameImpl(projectName, fixtureStack) { + ideFrameImpl(PROJECT_NAME, fixtureStack) { find(byXpath("//div[@javaclass='javax.swing.JLabel']")).findText(textToFind) .click() find(byXpath("//div[@tooltiptext.key='tooltip.close.notification']")).click() @@ -437,7 +438,7 @@ class CancelHoldReleaseJobTest { * Creates empty working set. */ private fun createWS(remoteRobot: RemoteRobot) = with(remoteRobot) { - ideFrameImpl(projectName, fixtureStack) { + ideFrameImpl(PROJECT_NAME, fixtureStack) { createWSFromContextMenu(fixtureStack, closableFixtureCollector) addWorkingSetDialog(fixtureStack) { addWorkingSet(wsName, connectionName) @@ -457,7 +458,7 @@ class CancelHoldReleaseJobTest { * Creates job in dataset. */ private fun createJob(testInfo: TestInfo, remoteRobot: RemoteRobot) = with(remoteRobot) { - openLocalFileAndCopyContent(filePath + fileName, projectName, fixtureStack, remoteRobot) + openLocalFileAndCopyContent(filePath + fileName, fixtureStack, remoteRobot) Thread.sleep(3000) createMemberAndPasteContentWithMock(testInfo, datasetName, jobName, fileName, remoteRobot) } @@ -493,10 +494,10 @@ class CancelHoldReleaseJobTest { ) createEmptyDatasetMember( - datasetName, memberName, projectName, fixtureStack, remoteRobot + datasetName, memberName, fixtureStack, remoteRobot ) isFirstRequest = false - pasteContent(memberName, projectName, fixtureStack, remoteRobot) + pasteContent(memberName, fixtureStack, remoteRobot) Thread.sleep(3000) isFirst = false } @@ -576,7 +577,7 @@ class CancelHoldReleaseJobTest { Pair("port", mockServer.port.toString()), Pair("jobName", jobName), Pair("retCode", rc), Pair("jobStatus", jobStatus.name)))) } ) - submitJob(jobName, projectName, fixtureStack, remoteRobot) + submitJob(jobName, fixtureStack, remoteRobot) responseDispatcher.removeAllEndpoints() } diff --git a/src/uiTest/kotlin/jes/JesWorkingSetViaActionButtonTest.kt b/src/uiTest/kotlin/jes/JesWorkingSetViaActionButtonTest.kt index f45108d61..4864d12c8 100644 --- a/src/uiTest/kotlin/jes/JesWorkingSetViaActionButtonTest.kt +++ b/src/uiTest/kotlin/jes/JesWorkingSetViaActionButtonTest.kt @@ -26,6 +26,8 @@ import org.junit.jupiter.api.* import org.junit.jupiter.api.Assertions.assertFalse import org.junit.jupiter.api.Assertions.assertTrue import org.junit.jupiter.api.extension.ExtendWith +import workingset.EMPTY_DATASET_MESSAGE +import workingset.PROJECT_NAME import java.time.Duration @@ -49,7 +51,7 @@ class JesWorkingSetViaActionButtonTest { @BeforeAll fun setUpAll(remoteRobot: RemoteRobot) { startMockServer() - setUpTestEnvironment(projectName, fixtureStack, closableFixtureCollector, remoteRobot) + setUpTestEnvironment(fixtureStack, closableFixtureCollector, remoteRobot) } /** @@ -58,8 +60,8 @@ class JesWorkingSetViaActionButtonTest { @AfterAll fun tearDownAll(remoteRobot: RemoteRobot) = with(remoteRobot) { mockServer.shutdown() - clearEnvironment(projectName, fixtureStack, closableFixtureCollector, remoteRobot) - ideFrameImpl(projectName, fixtureStack) { + clearEnvironment(fixtureStack, closableFixtureCollector, remoteRobot) + ideFrameImpl(PROJECT_NAME, fixtureStack) { close() } } @@ -91,7 +93,7 @@ class JesWorkingSetViaActionButtonTest { { it?.requestLine?.contains("zosmf/resttopology/systems") ?: false }, { MockResponse().setBody(responseDispatcher.readMockJson("infoResponse") ?: "") } ) - ideFrameImpl(projectName, fixtureStack) { + ideFrameImpl(PROJECT_NAME, fixtureStack) { createJesWorkingSetFromActionButton(closableFixtureCollector, fixtureStack) try { @@ -138,7 +140,7 @@ class JesWorkingSetViaActionButtonTest { @Order(2) fun testAddEmptyJesWorkingSetWithVeryLongNameViaActionButton(remoteRobot: RemoteRobot) = with(remoteRobot) { val jwsName: String = "B".repeat(200) - ideFrameImpl(projectName, fixtureStack) { + ideFrameImpl(PROJECT_NAME, fixtureStack) { createJesWorkingSetFromActionButton(closableFixtureCollector, fixtureStack) addJesWorkingSetDialog(fixtureStack) { addJesWorkingSet(jwsName, connectionName) @@ -163,7 +165,7 @@ class JesWorkingSetViaActionButtonTest { fun testAddJesWorkingSetWithOneValidFilterViaActionButton(remoteRobot: RemoteRobot) = with(remoteRobot) { val jwsName = "JWS1" val filter = Triple("*", ZOS_USERID, "") - ideFrameImpl(projectName, fixtureStack) { + ideFrameImpl(PROJECT_NAME, fixtureStack) { createJesWorkingSetFromActionButton(closableFixtureCollector, fixtureStack) addJesWorkingSetDialog(fixtureStack) { addJesWorkingSet(jwsName, connectionName, ZOS_USERID, filter) @@ -188,7 +190,7 @@ class JesWorkingSetViaActionButtonTest { { it?.requestLine?.contains("/zosmf/restjobs/jobs") ?: false }, { MockResponse().setBody("[]") } ) - ideFrameImpl(projectName, fixtureStack) { + ideFrameImpl(PROJECT_NAME, fixtureStack) { createJesWorkingSetFromActionButton(closableFixtureCollector, fixtureStack) addJesWorkingSetDialog(fixtureStack) { addJesWorkingSet(jwsName, connectionName, ZOS_USERID, validJobsFilters) @@ -197,12 +199,12 @@ class JesWorkingSetViaActionButtonTest { } closableFixtureCollector.closeOnceIfExists(AddJesWorkingSetDialog.name) } - openOrCloseJesWorkingSetInExplorer(jwsName, projectName, fixtureStack, remoteRobot) + openOrCloseJesWorkingSetInExplorer(jwsName, fixtureStack, remoteRobot) validJobsFilters.forEach { - openJobFilterInExplorer(it, "", projectName, fixtureStack, remoteRobot) - closeFilterInExplorer(it, projectName, fixtureStack, remoteRobot) + openJobFilterInExplorer(it, "", fixtureStack, remoteRobot) + closeFilterInExplorer(it, fixtureStack, remoteRobot) } - openOrCloseJesWorkingSetInExplorer(jwsName, projectName, fixtureStack, remoteRobot) + openOrCloseJesWorkingSetInExplorer(jwsName, fixtureStack, remoteRobot) } // TODO: eliminate ZOS_USERID @@ -213,7 +215,7 @@ class JesWorkingSetViaActionButtonTest { @Order(5) fun testAddJesWorkingSetWithInvalidFiltersViaActionButton(remoteRobot: RemoteRobot) = with(remoteRobot) { val jwsName = "JWS3" - ideFrameImpl(projectName, fixtureStack) { + ideFrameImpl(PROJECT_NAME, fixtureStack) { createJesWorkingSetFromActionButton(closableFixtureCollector, fixtureStack) addJesWorkingSetDialog(fixtureStack) { addJesWorkingSet(jwsName, connectionName) @@ -254,7 +256,7 @@ class JesWorkingSetViaActionButtonTest { @Order(6) fun testAddJesWorkingSetWithTheSameFiltersViaActionButton(remoteRobot: RemoteRobot) = with(remoteRobot) { val jwsName = "JWS3" - ideFrameImpl(projectName, fixtureStack) { + ideFrameImpl(PROJECT_NAME, fixtureStack) { createJesWorkingSetFromActionButton(closableFixtureCollector, fixtureStack) addJesWorkingSetDialog(fixtureStack) { addJesWorkingSet(jwsName, connectionName, ZOS_USERID, Triple("*", ZOS_USERID.lowercase(), "")) @@ -291,7 +293,6 @@ class JesWorkingSetViaActionButtonTest { { MockResponse().setBody("[]") } ) createConnection( - projectName, fixtureStack, closableFixtureCollector, "invalid_connection", @@ -300,7 +301,7 @@ class JesWorkingSetViaActionButtonTest { "https://${mockServer.hostName}:$testPort" ) val jwsName = "JWS3" - ideFrameImpl(projectName, fixtureStack) { + ideFrameImpl(PROJECT_NAME, fixtureStack) { createJesWorkingSetFromActionButton(closableFixtureCollector, fixtureStack) addJesWorkingSetDialog(fixtureStack) { addJesWorkingSet(jwsName, "invalid_connection", ZOS_USERID, Triple("*", ZOS_USERID, "")) @@ -309,13 +310,13 @@ class JesWorkingSetViaActionButtonTest { } closableFixtureCollector.closeOnceIfExists(AddJesWorkingSetDialog.name) } - openOrCloseJesWorkingSetInExplorer(jwsName, projectName, fixtureStack, remoteRobot) + openOrCloseJesWorkingSetInExplorer(jwsName, fixtureStack, remoteRobot) findAll(byXpath("//div[@class='MyComponent'][.//div[@accessiblename='Invalid URL port: \"104431\"' and @class='JEditorPane']]")).forEach { it.click() findAll( byXpath("//div[@class='ActionButton' and @myicon= 'close.svg']") ).first().click() } - openOrCloseJesWorkingSetInExplorer(jwsName, projectName, fixtureStack, remoteRobot) + openOrCloseJesWorkingSetInExplorer(jwsName, fixtureStack, remoteRobot) } } diff --git a/src/uiTest/kotlin/jes/JesWorkingSetViaContextMenuTest.kt b/src/uiTest/kotlin/jes/JesWorkingSetViaContextMenuTest.kt index 75268e6d5..8345bd081 100644 --- a/src/uiTest/kotlin/jes/JesWorkingSetViaContextMenuTest.kt +++ b/src/uiTest/kotlin/jes/JesWorkingSetViaContextMenuTest.kt @@ -28,6 +28,8 @@ import org.junit.jupiter.api.* import org.junit.jupiter.api.Assertions.assertFalse import org.junit.jupiter.api.Assertions.assertTrue import org.junit.jupiter.api.extension.ExtendWith +import workingset.EMPTY_DATASET_MESSAGE +import workingset.PROJECT_NAME import java.awt.event.KeyEvent import java.time.Duration @@ -53,7 +55,7 @@ class JesWorkingSetViaContextMenuTest { @BeforeAll fun setUpAll(remoteRobot: RemoteRobot) { startMockServer() - setUpTestEnvironment(projectName, fixtureStack, closableFixtureCollector, remoteRobot) + setUpTestEnvironment(fixtureStack, closableFixtureCollector, remoteRobot) } /** @@ -62,8 +64,8 @@ class JesWorkingSetViaContextMenuTest { @AfterAll fun tearDownAll(remoteRobot: RemoteRobot) = with(remoteRobot) { mockServer.shutdown() - clearEnvironment(projectName, fixtureStack, closableFixtureCollector, remoteRobot) - ideFrameImpl(projectName, fixtureStack) { + clearEnvironment(fixtureStack, closableFixtureCollector, remoteRobot) + ideFrameImpl(PROJECT_NAME, fixtureStack) { close() } } @@ -95,7 +97,7 @@ class JesWorkingSetViaContextMenuTest { { it?.requestLine?.contains("zosmf/resttopology/systems") ?: false }, { MockResponse().setBody(responseDispatcher.readMockJson("infoResponse") ?: "") } ) - ideFrameImpl(projectName, fixtureStack) { + ideFrameImpl(PROJECT_NAME, fixtureStack) { createJWSFromContextMenu(fixtureStack, closableFixtureCollector) try { if (dialog("Add JES Working Set Dialog").isShowing) { @@ -154,7 +156,7 @@ class JesWorkingSetViaContextMenuTest { fun testAddJesWorkingSetWithOneValidFilterViaContextMenu(remoteRobot: RemoteRobot) = with(remoteRobot) { val jwsName = "JWS1" val filter = Triple("*", ZOS_USERID, "") - ideFrameImpl(projectName, fixtureStack) { + ideFrameImpl(PROJECT_NAME, fixtureStack) { createJWSFromContextMenu(fixtureStack, closableFixtureCollector) addJesWorkingSetDialog(fixtureStack) { addJesWorkingSet(jwsName, connectionName, ZOS_USERID, filter) @@ -182,7 +184,7 @@ class JesWorkingSetViaContextMenuTest { @Order(5) fun testAddJWSWithInvalidFiltersViaContextMenu(remoteRobot: RemoteRobot) = with(remoteRobot) { val jwsName = "JWS2" - ideFrameImpl(projectName, fixtureStack) { + ideFrameImpl(PROJECT_NAME, fixtureStack) { createJWSFromContextMenu(fixtureStack, closableFixtureCollector) addJesWorkingSetDialog(fixtureStack) { addJesWorkingSet(jwsName, connectionName) @@ -228,7 +230,7 @@ class JesWorkingSetViaContextMenuTest { { it?.requestLine?.contains("/zosmf/restjobs/jobs") ?: false }, { MockResponse().setBody("[]") } ) - ideFrameImpl(projectName, fixtureStack) { + ideFrameImpl(PROJECT_NAME, fixtureStack) { createJWSFromContextMenu(fixtureStack, closableFixtureCollector) addJesWorkingSetDialog(fixtureStack) { addJesWorkingSet(jwsName, connectionName, ZOS_USERID, validJobsFilters) @@ -237,12 +239,12 @@ class JesWorkingSetViaContextMenuTest { } closableFixtureCollector.closeOnceIfExists(AddJesWorkingSetDialog.name) } - openOrCloseJesWorkingSetInExplorer(jwsName, projectName, fixtureStack, remoteRobot) + openOrCloseJesWorkingSetInExplorer(jwsName, fixtureStack, remoteRobot) validJobsFilters.forEach { - openJobFilterInExplorer(it, "", projectName, fixtureStack, remoteRobot) - closeFilterInExplorer(it, projectName, fixtureStack, remoteRobot) + openJobFilterInExplorer(it, "", fixtureStack, remoteRobot) + closeFilterInExplorer(it, fixtureStack, remoteRobot) } - openOrCloseJesWorkingSetInExplorer(jwsName, projectName, fixtureStack, remoteRobot) + openOrCloseJesWorkingSetInExplorer(jwsName, fixtureStack, remoteRobot) } // TODO: eliminate ZOS_USERID @@ -266,7 +268,6 @@ class JesWorkingSetViaContextMenuTest { { MockResponse().setBody("[]") } ) createConnection( - projectName, fixtureStack, closableFixtureCollector, "invalid connection", @@ -274,7 +275,7 @@ class JesWorkingSetViaContextMenuTest { remoteRobot, "https://${mockServer.hostName}:$testPort" ) - ideFrameImpl(projectName, fixtureStack) { + ideFrameImpl(PROJECT_NAME, fixtureStack) { createJWSFromContextMenu(fixtureStack, closableFixtureCollector) addJesWorkingSetDialog(fixtureStack) { addJesWorkingSet(jwsName, "invalid connection", ZOS_USERID, Triple("*", ZOS_USERID, "")) @@ -283,14 +284,14 @@ class JesWorkingSetViaContextMenuTest { } closableFixtureCollector.closeOnceIfExists(AddJesWorkingSetDialog.name) } - openOrCloseJesWorkingSetInExplorer(jwsName, projectName, fixtureStack, remoteRobot) + openOrCloseJesWorkingSetInExplorer(jwsName, fixtureStack, remoteRobot) findAll(byXpath("//div[@class='MyComponent'][.//div[@accessiblename='Invalid URL port: \"104431\"' and @class='JEditorPane']]")).forEach { it.click() findAll( byXpath("//div[@class='ActionButton' and @myicon= 'close.svg']") ).first().click() } - openOrCloseJesWorkingSetInExplorer(jwsName, projectName, fixtureStack, remoteRobot) + openOrCloseJesWorkingSetInExplorer(jwsName, fixtureStack, remoteRobot) } // TODO: eliminate ZOS_USERID @@ -301,7 +302,7 @@ class JesWorkingSetViaContextMenuTest { @Order(8) fun testAddJWSWithTheSameFiltersViaContextMenu(remoteRobot: RemoteRobot) = with(remoteRobot) { val jwsName = "JWS4" - ideFrameImpl(projectName, fixtureStack) { + ideFrameImpl(PROJECT_NAME, fixtureStack) { createJWSFromContextMenu(fixtureStack, closableFixtureCollector) addJesWorkingSetDialog(fixtureStack) { addJesWorkingSet(jwsName, connectionName, ZOS_USERID, Triple("*", ZOS_USERID.lowercase(), "")) @@ -325,7 +326,7 @@ class JesWorkingSetViaContextMenuTest { @Order(9) fun testCreateInvalidFiltersViaContextMenu(remoteRobot: RemoteRobot) = with(remoteRobot) { val jwsName = "first jws" - ideFrameImpl(projectName, fixtureStack) { + ideFrameImpl(PROJECT_NAME, fixtureStack) { createJobsFilter(jwsName, fixtureStack, closableFixtureCollector) createJobsFilterDialog(fixtureStack) { invalidJobsFiltersMap.forEach { @@ -367,12 +368,12 @@ class JesWorkingSetViaContextMenuTest { validJobsFilters.forEach { createFilterFromContextMenu(jwsName, it, remoteRobot) } - openOrCloseJesWorkingSetInExplorer(jwsName, projectName, fixtureStack, remoteRobot) + openOrCloseJesWorkingSetInExplorer(jwsName, fixtureStack, remoteRobot) validJobsFilters.forEach { - openJobFilterInExplorer(it, "", projectName, fixtureStack, remoteRobot) - closeFilterInExplorer(it, projectName, fixtureStack, remoteRobot) + openJobFilterInExplorer(it, "", fixtureStack, remoteRobot) + closeFilterInExplorer(it, fixtureStack, remoteRobot) } - openOrCloseJesWorkingSetInExplorer(jwsName, projectName, fixtureStack, remoteRobot) + openOrCloseJesWorkingSetInExplorer(jwsName, fixtureStack, remoteRobot) } /** @@ -382,7 +383,7 @@ class JesWorkingSetViaContextMenuTest { @Order(11) fun testCreateAlreadyExistsFilterViaContextMenu(remoteRobot: RemoteRobot) = with(remoteRobot) { val jwsName = "first jws" - ideFrameImpl(projectName, fixtureStack) { + ideFrameImpl(PROJECT_NAME, fixtureStack) { createJobsFilter(jwsName, fixtureStack, closableFixtureCollector) createJobsFilterDialog(fixtureStack) { createJobsFilter(Triple("*", ZOS_USERID, "")) @@ -413,9 +414,9 @@ class JesWorkingSetViaContextMenuTest { { it?.requestLine?.contains("/zosmf/restjobs/jobs") ?: false }, { MockResponse().setBody("[]") } ) - openOrCloseJesWorkingSetInExplorer(jwsName, projectName, fixtureStack, remoteRobot) - closeFilterInExplorer(Triple("*", ZOS_USERID, ""), projectName, fixtureStack, remoteRobot) - ideFrameImpl(projectName, fixtureStack) { + openOrCloseJesWorkingSetInExplorer(jwsName, fixtureStack, remoteRobot) + closeFilterInExplorer(Triple("*", ZOS_USERID, ""), fixtureStack, remoteRobot) + ideFrameImpl(PROJECT_NAME, fixtureStack) { editJWSFromContextMenu(jwsName, fixtureStack, closableFixtureCollector) editJesWorkingSetDialog(fixtureStack) { addFilter(ZOS_USERID, Triple("*", "$ZOS_USERID*", "")) @@ -424,8 +425,8 @@ class JesWorkingSetViaContextMenuTest { } closableFixtureCollector.closeOnceIfExists(EditJesWorkingSetDialog.name) } - openJobFilterInExplorer(Triple("*", ZOS_USERID, ""), "", projectName, fixtureStack, remoteRobot) - openOrCloseJesWorkingSetInExplorer(jwsName, projectName, fixtureStack, remoteRobot) + openJobFilterInExplorer(Triple("*", ZOS_USERID, ""), "", fixtureStack, remoteRobot) + openOrCloseJesWorkingSetInExplorer(jwsName, fixtureStack, remoteRobot) } /** @@ -437,8 +438,8 @@ class JesWorkingSetViaContextMenuTest { val jwsName = "first jws" val jobsFilter = Triple("*", ZOS_USERID, "") - openOrCloseJesWorkingSetInExplorer(jwsName, projectName, fixtureStack, remoteRobot) - ideFrameImpl(projectName, fixtureStack) { + openOrCloseJesWorkingSetInExplorer(jwsName, fixtureStack, remoteRobot) + ideFrameImpl(PROJECT_NAME, fixtureStack) { editJWSFromContextMenu(jwsName, fixtureStack, closableFixtureCollector) editJesWorkingSetDialog(fixtureStack) { deleteFilter(jobsFilter) @@ -447,8 +448,8 @@ class JesWorkingSetViaContextMenuTest { } closableFixtureCollector.closeOnceIfExists(EditJesWorkingSetDialog.name) } - checkFilterWasDeletedJWSRefreshed(jobsFilter, projectName, fixtureStack, remoteRobot) - openOrCloseJesWorkingSetInExplorer(jwsName, projectName, fixtureStack, remoteRobot) + checkFilterWasDeletedJWSRefreshed(jobsFilter, fixtureStack, remoteRobot) + openOrCloseJesWorkingSetInExplorer(jwsName, fixtureStack, remoteRobot) } /** @@ -469,8 +470,8 @@ class JesWorkingSetViaContextMenuTest { Triple("TEST1", ZOS_USERID, ""), Triple("TEST***", "$ZOS_USERID***", "") ) - openOrCloseJesWorkingSetInExplorer(jwsName, projectName, fixtureStack, remoteRobot) - ideFrameImpl(projectName, fixtureStack) { + openOrCloseJesWorkingSetInExplorer(jwsName, fixtureStack, remoteRobot) + ideFrameImpl(PROJECT_NAME, fixtureStack) { editJWSFromContextMenu(jwsName, fixtureStack, closableFixtureCollector) editJesWorkingSetDialog(fixtureStack) { deleteAllFilters() @@ -483,8 +484,8 @@ class JesWorkingSetViaContextMenuTest { } closableFixtureCollector.closeOnceIfExists(EditJesWorkingSetDialog.name) } - deletedFilters.forEach { checkFilterWasDeletedJWSRefreshed(it, projectName, fixtureStack, remoteRobot) } - openOrCloseJesWorkingSetInExplorer(jwsName, projectName, fixtureStack, remoteRobot) + deletedFilters.forEach { checkFilterWasDeletedJWSRefreshed(it, fixtureStack, remoteRobot) } + openOrCloseJesWorkingSetInExplorer(jwsName, fixtureStack, remoteRobot) } /** @@ -495,8 +496,8 @@ class JesWorkingSetViaContextMenuTest { fun testEditJWSChangeConnectionToInvalidViaContextMenu(remoteRobot: RemoteRobot) = with(remoteRobot) { val newConnectionName = "invalid connection" val jwsName = "JWS1" - openOrCloseJesWorkingSetInExplorer(jwsName, projectName, fixtureStack, remoteRobot) - ideFrameImpl(projectName, fixtureStack) { + openOrCloseJesWorkingSetInExplorer(jwsName, fixtureStack, remoteRobot) + ideFrameImpl(PROJECT_NAME, fixtureStack) { editJWSFromContextMenu(jwsName, fixtureStack, closableFixtureCollector) editJesWorkingSetDialog(fixtureStack) { changeConnection(newConnectionName) @@ -514,7 +515,6 @@ class JesWorkingSetViaContextMenuTest { openJobFilterInExplorer( Triple("*", ZOS_USERID, ""), "Invalid URL port: \"104431\"", - projectName, fixtureStack, remoteRobot ) @@ -544,7 +544,6 @@ class JesWorkingSetViaContextMenuTest { { MockResponse().setBody("[]") } ) createConnection( - projectName, fixtureStack, closableFixtureCollector, newConnectionName, @@ -553,7 +552,7 @@ class JesWorkingSetViaContextMenuTest { "https://${mockServer.hostName}:${mockServer.port}" ) val jwsName = "JWS1" - ideFrameImpl(projectName, fixtureStack) { + ideFrameImpl(PROJECT_NAME, fixtureStack) { editJWSFromContextMenu(jwsName, fixtureStack, closableFixtureCollector) editJesWorkingSetDialog(fixtureStack) { changeConnection(newConnectionName) @@ -562,8 +561,8 @@ class JesWorkingSetViaContextMenuTest { } closableFixtureCollector.closeOnceIfExists(EditJesWorkingSetDialog.name) } - checkItemWasDeletedWSRefreshed("Invalid URL port: \"104431\"", projectName, fixtureStack, remoteRobot) - openOrCloseJesWorkingSetInExplorer(jwsName, projectName, fixtureStack, remoteRobot) + checkItemWasDeletedWSRefreshed("Invalid URL port: \"104431\"", fixtureStack, remoteRobot) + openOrCloseJesWorkingSetInExplorer(jwsName, fixtureStack, remoteRobot) } /** @@ -575,8 +574,8 @@ class JesWorkingSetViaContextMenuTest { val newJesWorkingSetName = "new jws name" val oldJesWorkingSetName = "JWS1" val alreadyExistsJesWorkingSetName = "JWS2" - openOrCloseJesWorkingSetInExplorer(oldJesWorkingSetName, projectName, fixtureStack, remoteRobot) - ideFrameImpl(projectName, fixtureStack) { + openOrCloseJesWorkingSetInExplorer(oldJesWorkingSetName, fixtureStack, remoteRobot) + ideFrameImpl(PROJECT_NAME, fixtureStack) { editJWSFromContextMenu(oldJesWorkingSetName, fixtureStack, closableFixtureCollector) editJesWorkingSetDialog(fixtureStack) { renameJesWorkingSet(alreadyExistsJesWorkingSetName) @@ -592,8 +591,8 @@ class JesWorkingSetViaContextMenuTest { } closableFixtureCollector.closeOnceIfExists(EditJesWorkingSetDialog.name) } - checkItemWasDeletedWSRefreshed(oldJesWorkingSetName, projectName, fixtureStack, remoteRobot) - openOrCloseJesWorkingSetInExplorer(newJesWorkingSetName, projectName, fixtureStack, remoteRobot) + checkItemWasDeletedWSRefreshed(oldJesWorkingSetName, fixtureStack, remoteRobot) + openOrCloseJesWorkingSetInExplorer(newJesWorkingSetName, fixtureStack, remoteRobot) } /** @@ -610,11 +609,11 @@ class JesWorkingSetViaContextMenuTest { { it?.requestLine?.contains("/zosmf/restjobs/jobs") ?: false }, { MockResponse().setBody("[]") } ) - openOrCloseJesWorkingSetInExplorer(jwsName, projectName, fixtureStack, remoteRobot) + openOrCloseJesWorkingSetInExplorer(jwsName, fixtureStack, remoteRobot) editJobFilter(oldFilter, newFilter, remoteRobot) - checkFilterWasDeletedJWSRefreshed(oldFilter, projectName, fixtureStack, remoteRobot) - openJobFilterInExplorer(newFilter, "", projectName, fixtureStack, remoteRobot) - openOrCloseJesWorkingSetInExplorer(jwsName, projectName, fixtureStack, remoteRobot) + checkFilterWasDeletedJWSRefreshed(oldFilter, fixtureStack, remoteRobot) + openJobFilterInExplorer(newFilter, "", fixtureStack, remoteRobot) + openOrCloseJesWorkingSetInExplorer(jwsName, fixtureStack, remoteRobot) } /** @@ -633,11 +632,11 @@ class JesWorkingSetViaContextMenuTest { @Order(20) fun testDeleteJWSViaContextMenu(remoteRobot: RemoteRobot) = with(remoteRobot) { val jwsName = "JWS2" - ideFrameImpl(projectName, fixtureStack) { + ideFrameImpl(PROJECT_NAME, fixtureStack) { deleteJWSFromContextMenu(jwsName) clickButton("Yes") } - checkItemWasDeletedWSRefreshed(jwsName, projectName, fixtureStack, remoteRobot) + checkItemWasDeletedWSRefreshed(jwsName, fixtureStack, remoteRobot) } /** @@ -647,7 +646,7 @@ class JesWorkingSetViaContextMenuTest { @Order(21) fun testDeleteAllJWSViaContextMenu(remoteRobot: RemoteRobot) = with(remoteRobot) { val jwsList = listOf("first jws", "A".repeat(200), "B12#$%^&*", "new jws name", "JWS3") - ideFrameImpl(projectName, fixtureStack) { + ideFrameImpl(PROJECT_NAME, fixtureStack) { explorer { jesExplorer.click() find(viewTree).click() @@ -674,7 +673,7 @@ class JesWorkingSetViaContextMenuTest { remoteRobot: RemoteRobot ) = with(remoteRobot) { val textToFind = convertJobFilterToString(jobFilter) - ideFrameImpl(projectName, fixtureStack) { + ideFrameImpl(PROJECT_NAME, fixtureStack) { explorer { jesExplorer.click() find(viewTree).findText(jwsName).doubleClick() @@ -686,7 +685,7 @@ class JesWorkingSetViaContextMenuTest { dialog("Deletion Of Jobs Filter") { clickButton("Yes") } - checkFilterWasDeletedJWSRefreshed(jobFilter, projectName, fixtureStack, remoteRobot) + checkFilterWasDeletedJWSRefreshed(jobFilter, fixtureStack, remoteRobot) explorer { find(viewTree).findText(jwsName).doubleClick() } @@ -711,7 +710,7 @@ class JesWorkingSetViaContextMenuTest { remoteRobot: RemoteRobot ) = with(remoteRobot) { val textToFind = convertJobFilterToString(oldFilter) - ideFrameImpl(projectName, fixtureStack) { + ideFrameImpl(PROJECT_NAME, fixtureStack) { explorer { jesExplorer.click() find(viewTree).findText(textToFind).rightClick() @@ -731,7 +730,7 @@ class JesWorkingSetViaContextMenuTest { * Creates empty JES working set from context menu. */ private fun createJWS(jwsName: String, isUniqueName: Boolean, remoteRobot: RemoteRobot) = with(remoteRobot) { - ideFrameImpl(projectName, fixtureStack) { + ideFrameImpl(PROJECT_NAME, fixtureStack) { createJWSFromContextMenu(fixtureStack, closableFixtureCollector) addJesWorkingSetDialog(fixtureStack) { addJesWorkingSet(jwsName, connectionName) @@ -766,7 +765,7 @@ class JesWorkingSetViaContextMenuTest { remoteRobot: RemoteRobot ) = with(remoteRobot) { - ideFrameImpl(projectName, fixtureStack) { + ideFrameImpl(PROJECT_NAME, fixtureStack) { createJobsFilter(jwsName, fixtureStack, closableFixtureCollector) createJobsFilterDialog(fixtureStack) { createJobsFilter(filter) diff --git a/src/uiTest/kotlin/jes/JesWorkingSetViaSettingsTest.kt b/src/uiTest/kotlin/jes/JesWorkingSetViaSettingsTest.kt index e904a1585..e102f99c3 100644 --- a/src/uiTest/kotlin/jes/JesWorkingSetViaSettingsTest.kt +++ b/src/uiTest/kotlin/jes/JesWorkingSetViaSettingsTest.kt @@ -24,6 +24,8 @@ import okhttp3.mockwebserver.MockResponse import org.junit.jupiter.api.* import org.junit.jupiter.api.Assertions.assertFalse import org.junit.jupiter.api.extension.ExtendWith +import workingset.EMPTY_DATASET_MESSAGE +import workingset.PROJECT_NAME import java.time.Duration /** @@ -38,7 +40,6 @@ class JesWorkingSetViaSettingsTest { private var wantToClose = mutableListOf( "Settings Dialog", "Add JES Working Set Dialog", "Edit JES Working Set Dialog" ) - private val projectName = "untitled" private val connectionName = "valid connection" @@ -48,7 +49,7 @@ class JesWorkingSetViaSettingsTest { @BeforeAll fun setUpAll(remoteRobot: RemoteRobot) { startMockServer() - setUpTestEnvironment(projectName, fixtureStack, closableFixtureCollector, remoteRobot) + setUpTestEnvironment(fixtureStack, closableFixtureCollector, remoteRobot) } /** @@ -57,8 +58,8 @@ class JesWorkingSetViaSettingsTest { @AfterAll fun tearDownAll(remoteRobot: RemoteRobot) = with(remoteRobot) { mockServer.shutdown() - clearEnvironment(projectName, fixtureStack, closableFixtureCollector, remoteRobot) - ideFrameImpl(projectName, fixtureStack) { + clearEnvironment(fixtureStack, closableFixtureCollector, remoteRobot) + ideFrameImpl(PROJECT_NAME, fixtureStack) { close() } } @@ -78,7 +79,7 @@ class JesWorkingSetViaSettingsTest { @Test @Order(1) fun testAddJesWorkingSetWithoutConnectionViaSettings(remoteRobot: RemoteRobot) = with(remoteRobot) { - ideFrameImpl(projectName, fixtureStack) { + ideFrameImpl(PROJECT_NAME, fixtureStack) { explorer { settings(closableFixtureCollector, fixtureStack) } @@ -112,7 +113,6 @@ class JesWorkingSetViaSettingsTest { createValidConnectionWithMock( testInfo, connectionName, - projectName, fixtureStack, closableFixtureCollector, remoteRobot @@ -130,7 +130,7 @@ class JesWorkingSetViaSettingsTest { fun testAddJesWorkingSetWithOneValidFilterViaSettings(remoteRobot: RemoteRobot) = with(remoteRobot) { val jwsName = "JWS1" val filter = Triple("*", ZOS_USERID, "") - ideFrameImpl(projectName, fixtureStack) { + ideFrameImpl(PROJECT_NAME, fixtureStack) { explorer { settings(closableFixtureCollector, fixtureStack) } @@ -168,7 +168,7 @@ class JesWorkingSetViaSettingsTest { @Order(5) fun testAddJWSWithInvalidFiltersViaSettings(remoteRobot: RemoteRobot) = with(remoteRobot) { val jwsName = "JWS2" - ideFrameImpl(projectName, fixtureStack) { + ideFrameImpl(PROJECT_NAME, fixtureStack) { explorer { jesExplorer.click() settings(closableFixtureCollector, fixtureStack) @@ -225,7 +225,7 @@ class JesWorkingSetViaSettingsTest { { it?.requestLine?.contains("/zosmf/restjobs/jobs") ?: false }, { MockResponse().setBody("[]").setResponseCode(200) } ) - ideFrameImpl(projectName, fixtureStack) { + ideFrameImpl(PROJECT_NAME, fixtureStack) { explorer { jesExplorer.click() settings(closableFixtureCollector, fixtureStack) @@ -245,12 +245,12 @@ class JesWorkingSetViaSettingsTest { } closableFixtureCollector.closeOnceIfExists(SettingsDialog.name) } - openOrCloseJesWorkingSetInExplorer(jwsName, projectName, fixtureStack, remoteRobot) + openOrCloseJesWorkingSetInExplorer(jwsName, fixtureStack, remoteRobot) validJobsFilters.forEach { - openJobFilterInExplorer(it, "", projectName, fixtureStack, remoteRobot) - closeFilterInExplorer(it, projectName, fixtureStack, remoteRobot) + openJobFilterInExplorer(it, "", fixtureStack, remoteRobot) + closeFilterInExplorer(it, fixtureStack, remoteRobot) } - openOrCloseJesWorkingSetInExplorer(jwsName, projectName, fixtureStack, remoteRobot) + openOrCloseJesWorkingSetInExplorer(jwsName, fixtureStack, remoteRobot) } // TODO: eliminate ZOS_USERID @@ -273,7 +273,6 @@ class JesWorkingSetViaSettingsTest { ) createConnection( - projectName, fixtureStack, closableFixtureCollector, "invalid_connection", @@ -282,7 +281,7 @@ class JesWorkingSetViaSettingsTest { "https://${mockServer.hostName}:$testPort" ) val jwsName = "JWS3" - ideFrameImpl(projectName, fixtureStack) { + ideFrameImpl(PROJECT_NAME, fixtureStack) { explorer { settings(closableFixtureCollector, fixtureStack) } @@ -301,14 +300,14 @@ class JesWorkingSetViaSettingsTest { } closableFixtureCollector.closeOnceIfExists(SettingsDialog.name) } - openOrCloseJesWorkingSetInExplorer(jwsName, projectName, fixtureStack, remoteRobot) + openOrCloseJesWorkingSetInExplorer(jwsName, fixtureStack, remoteRobot) findAll(byXpath("//div[@class='MyComponent'][.//div[@accessiblename='Invalid URL port: \"104431\"' and @class='JEditorPane']]")).forEach { it.click() findAll( byXpath("//div[@class='ActionButton' and @myicon= 'close.svg']") ).first().click() } - openOrCloseJesWorkingSetInExplorer(jwsName, projectName, fixtureStack, remoteRobot) + openOrCloseJesWorkingSetInExplorer(jwsName, fixtureStack, remoteRobot) } // TODO: eliminate ZOS_USERID @@ -319,7 +318,7 @@ class JesWorkingSetViaSettingsTest { @Order(8) fun testAddJWSWithTheSameFiltersViaSettings(remoteRobot: RemoteRobot) = with(remoteRobot) { val jwsName = "JWS4" - ideFrameImpl(projectName, fixtureStack) { + ideFrameImpl(PROJECT_NAME, fixtureStack) { explorer { settings(closableFixtureCollector, fixtureStack) } @@ -360,9 +359,9 @@ class JesWorkingSetViaSettingsTest { { it?.requestLine?.contains("/zosmf/restjobs/jobs") ?: false }, { MockResponse().setBody("[]") } ) - openOrCloseJesWorkingSetInExplorer(jwsName, projectName, fixtureStack, remoteRobot) - closeFilterInExplorer(Triple("*", ZOS_USERID, ""), projectName, fixtureStack, remoteRobot) - ideFrameImpl(projectName, fixtureStack) { + openOrCloseJesWorkingSetInExplorer(jwsName, fixtureStack, remoteRobot) + closeFilterInExplorer(Triple("*", ZOS_USERID, ""), fixtureStack, remoteRobot) + ideFrameImpl(PROJECT_NAME, fixtureStack) { explorer { settings(closableFixtureCollector, fixtureStack) } @@ -381,8 +380,8 @@ class JesWorkingSetViaSettingsTest { } closableFixtureCollector.closeOnceIfExists(SettingsDialog.name) } - openJobFilterInExplorer(newFilter, "", projectName, fixtureStack, remoteRobot) - openOrCloseJesWorkingSetInExplorer(jwsName, projectName, fixtureStack, remoteRobot) + openJobFilterInExplorer(newFilter, "", fixtureStack, remoteRobot) + openOrCloseJesWorkingSetInExplorer(jwsName, fixtureStack, remoteRobot) } /** @@ -394,8 +393,8 @@ class JesWorkingSetViaSettingsTest { val jwsName = "JWS2" val filtersToBeDeleted = listOf(Triple("*", ZOS_USERID, ""), Triple("TEST**", ZOS_USERID, ""), Triple("TEST***", ZOS_USERID, "")) - openOrCloseJesWorkingSetInExplorer(jwsName, projectName, fixtureStack, remoteRobot) - ideFrameImpl(projectName, fixtureStack) { + openOrCloseJesWorkingSetInExplorer(jwsName, fixtureStack, remoteRobot) + ideFrameImpl(PROJECT_NAME, fixtureStack) { explorer { settings(closableFixtureCollector, fixtureStack) } @@ -414,8 +413,8 @@ class JesWorkingSetViaSettingsTest { } closableFixtureCollector.closeOnceIfExists(SettingsDialog.name) } - filtersToBeDeleted.forEach { checkFilterWasDeletedJWSRefreshed(it, projectName, fixtureStack, remoteRobot) } - openOrCloseJesWorkingSetInExplorer(jwsName, projectName, fixtureStack, remoteRobot) + filtersToBeDeleted.forEach { checkFilterWasDeletedJWSRefreshed(it, fixtureStack, remoteRobot) } + openOrCloseJesWorkingSetInExplorer(jwsName, fixtureStack, remoteRobot) } /** @@ -434,8 +433,8 @@ class JesWorkingSetViaSettingsTest { Triple("TEST1", "$ZOS_USERID***", ""), Triple("TEST***", "$ZOS_USERID***", "") ) - openOrCloseJesWorkingSetInExplorer(jwsName, projectName, fixtureStack, remoteRobot) - ideFrameImpl(projectName, fixtureStack) { + openOrCloseJesWorkingSetInExplorer(jwsName, fixtureStack, remoteRobot) + ideFrameImpl(PROJECT_NAME, fixtureStack) { explorer { settings(closableFixtureCollector, fixtureStack) } @@ -458,8 +457,8 @@ class JesWorkingSetViaSettingsTest { } closableFixtureCollector.closeOnceIfExists(SettingsDialog.name) } - filtersToBeDeleted.forEach { checkFilterWasDeletedJWSRefreshed(it, projectName, fixtureStack, remoteRobot) } - openOrCloseJesWorkingSetInExplorer(jwsName, projectName, fixtureStack, remoteRobot) + filtersToBeDeleted.forEach { checkFilterWasDeletedJWSRefreshed(it, fixtureStack, remoteRobot) } + openOrCloseJesWorkingSetInExplorer(jwsName, fixtureStack, remoteRobot) } /** @@ -469,8 +468,8 @@ class JesWorkingSetViaSettingsTest { @Order(12) fun testEditJWSChangeConnectionToInvalidViaSettings(remoteRobot: RemoteRobot) = with(remoteRobot) { val jwsName = "JWS1" - openOrCloseJesWorkingSetInExplorer(jwsName, projectName, fixtureStack, remoteRobot) - ideFrameImpl(projectName, fixtureStack) { + openOrCloseJesWorkingSetInExplorer(jwsName, fixtureStack, remoteRobot) + ideFrameImpl(PROJECT_NAME, fixtureStack) { explorer { settings(closableFixtureCollector, fixtureStack) } @@ -498,7 +497,6 @@ class JesWorkingSetViaSettingsTest { openJobFilterInExplorer( Triple("*", ZOS_USERID, ""), "Invalid URL port: \"104431\"", - projectName, fixtureStack, remoteRobot ) @@ -529,7 +527,6 @@ class JesWorkingSetViaSettingsTest { { MockResponse().setBody("[]") } ) createConnection( - projectName, fixtureStack, closableFixtureCollector, newConnectionName, @@ -537,7 +534,7 @@ class JesWorkingSetViaSettingsTest { remoteRobot, "https://${mockServer.hostName}:${mockServer.port}" ) - ideFrameImpl(projectName, fixtureStack) { + ideFrameImpl(PROJECT_NAME, fixtureStack) { explorer { settings(closableFixtureCollector, fixtureStack) } @@ -556,8 +553,8 @@ class JesWorkingSetViaSettingsTest { } closableFixtureCollector.closeOnceIfExists(SettingsDialog.name) } - checkItemWasDeletedWSRefreshed("Invalid URL port: \"104431\"", projectName, fixtureStack, remoteRobot) - openOrCloseJesWorkingSetInExplorer(jwsName, projectName, fixtureStack, remoteRobot) + checkItemWasDeletedWSRefreshed("Invalid URL port: \"104431\"", fixtureStack, remoteRobot) + openOrCloseJesWorkingSetInExplorer(jwsName, fixtureStack, remoteRobot) } /** @@ -569,8 +566,8 @@ class JesWorkingSetViaSettingsTest { val newJesWorkingSetName = "new jws name" val oldJesWorkingSetName = "JWS1" val alreadyExistsJesWorkingSetName = "JWS2" - openOrCloseJesWorkingSetInExplorer(oldJesWorkingSetName, projectName, fixtureStack, remoteRobot) - ideFrameImpl(projectName, fixtureStack) { + openOrCloseJesWorkingSetInExplorer(oldJesWorkingSetName, fixtureStack, remoteRobot) + ideFrameImpl(PROJECT_NAME, fixtureStack) { explorer { settings(closableFixtureCollector, fixtureStack) } @@ -596,8 +593,8 @@ class JesWorkingSetViaSettingsTest { } closableFixtureCollector.closeOnceIfExists(SettingsDialog.name) } - checkItemWasDeletedWSRefreshed(oldJesWorkingSetName, projectName, fixtureStack, remoteRobot) - openOrCloseJesWorkingSetInExplorer(newJesWorkingSetName, projectName, fixtureStack, remoteRobot) + checkItemWasDeletedWSRefreshed(oldJesWorkingSetName, fixtureStack, remoteRobot) + openOrCloseJesWorkingSetInExplorer(newJesWorkingSetName, fixtureStack, remoteRobot) } /** @@ -607,7 +604,7 @@ class JesWorkingSetViaSettingsTest { @Order(15) fun testDeleteJWSViaSettings(remoteRobot: RemoteRobot) = with(remoteRobot) { val jwsName = "JWS2" - ideFrameImpl(projectName, fixtureStack) { + ideFrameImpl(PROJECT_NAME, fixtureStack) { explorer { settings(closableFixtureCollector, fixtureStack) } @@ -620,7 +617,7 @@ class JesWorkingSetViaSettingsTest { } closableFixtureCollector.closeOnceIfExists(SettingsDialog.name) } - checkItemWasDeletedWSRefreshed(jwsName, projectName, fixtureStack, remoteRobot) + checkItemWasDeletedWSRefreshed(jwsName, fixtureStack, remoteRobot) } /** @@ -629,7 +626,7 @@ class JesWorkingSetViaSettingsTest { @Test @Order(16) fun testDeleteAllJWSViaSettings(remoteRobot: RemoteRobot) = with(remoteRobot) { - ideFrameImpl(projectName, fixtureStack) { + ideFrameImpl(PROJECT_NAME, fixtureStack) { explorer { settings(closableFixtureCollector, fixtureStack) } @@ -649,7 +646,7 @@ class JesWorkingSetViaSettingsTest { * Creates empty JES working set via settings. */ private fun createJWS(jwsName: String, isUniqueName: Boolean, remoteRobot: RemoteRobot) = with(remoteRobot) { - ideFrameImpl(projectName, fixtureStack) { + ideFrameImpl(PROJECT_NAME, fixtureStack) { explorer { settings(closableFixtureCollector, fixtureStack) } diff --git a/src/uiTest/kotlin/jes/PurgeJobTest.kt b/src/uiTest/kotlin/jes/PurgeJobTest.kt index 74441c2cc..50690f1de 100644 --- a/src/uiTest/kotlin/jes/PurgeJobTest.kt +++ b/src/uiTest/kotlin/jes/PurgeJobTest.kt @@ -23,6 +23,8 @@ import io.kotest.assertions.throwables.shouldThrow import okhttp3.mockwebserver.MockResponse import org.junit.jupiter.api.* import org.junit.jupiter.api.extension.ExtendWith +import workingset.EMPTY_DATASET_MESSAGE +import workingset.PROJECT_NAME import java.awt.event.KeyEvent import java.io.File @@ -38,7 +40,6 @@ class PurgeJobTest { private var wantToClose = mutableListOf( "Allocate Dataset Dialog" ) - private val projectName = "untitled" private val connectionName = "valid connection" private val wsName = "WS1" @@ -56,11 +57,10 @@ class PurgeJobTest { @BeforeAll fun setUpAll(testInfo: TestInfo, remoteRobot: RemoteRobot) { startMockServer() - setUpTestEnvironment(projectName, fixtureStack, closableFixtureCollector, remoteRobot) + setUpTestEnvironment(fixtureStack, closableFixtureCollector, remoteRobot) createValidConnectionWithMock( testInfo, connectionName, - projectName, fixtureStack, closableFixtureCollector, remoteRobot @@ -87,7 +87,7 @@ class PurgeJobTest { MockResponse().setBody(buildListMembersJson()) } ) - allocatePDSAndCreateMask(wsName, datasetName, projectName, fixtureStack, closableFixtureCollector, remoteRobot) + allocatePDSAndCreateMask(wsName, datasetName, fixtureStack, closableFixtureCollector, remoteRobot) createJobs(testInfo, remoteRobot) createJWS(remoteRobot) } @@ -110,10 +110,10 @@ class PurgeJobTest { { it?.requestLine?.contains("DELETE /zosmf/restfiles/ds/${datasetName.uppercase()}") ?: false }, { MockResponse().setBody("{}") } ) - deleteDataset(datasetName, projectName, fixtureStack, remoteRobot) + deleteDataset(datasetName, fixtureStack, remoteRobot) mockServer.shutdown() - clearEnvironment(projectName, fixtureStack, closableFixtureCollector, remoteRobot) - ideFrameImpl(projectName, fixtureStack) { + clearEnvironment(fixtureStack, closableFixtureCollector, remoteRobot) + ideFrameImpl(PROJECT_NAME, fixtureStack) { close() } } @@ -162,7 +162,7 @@ class PurgeJobTest { Triple("TEST2", ZOS_USERID, ""), Triple("TEST3", ZOS_USERID, "") ) - ideFrameImpl(projectName, fixtureStack) { + ideFrameImpl(PROJECT_NAME, fixtureStack) { createJesWorkingSetFromActionButton(closableFixtureCollector, fixtureStack) addJesWorkingSetDialog(fixtureStack) { addJesWorkingSet(jwsName, connectionName, ZOS_USERID, filters) @@ -220,7 +220,7 @@ class PurgeJobTest { Thread.sleep(3000) checkNotificationJobPurged(jobName, jobId, true, remoteRobot) checkJobsOutputWasDeletedJWSRefreshed(jobName, jobId, remoteRobot) - closeFilterInExplorer(Triple(jobName, ZOS_USERID, ""), projectName, fixtureStack, remoteRobot) + closeFilterInExplorer(Triple(jobName, ZOS_USERID, ""), fixtureStack, remoteRobot) responseDispatcher.removeAllEndpoints() } @@ -230,7 +230,7 @@ class PurgeJobTest { private fun checkJobsOutputWasDeletedJWSRefreshed( jobName: String, jobId: String, remoteRobot: RemoteRobot ) = with(remoteRobot) { - ideFrameImpl(projectName, fixtureStack) { + ideFrameImpl(PROJECT_NAME, fixtureStack) { explorer { shouldThrow { find(viewTree).findText("$jobName ($jobId)") @@ -243,7 +243,7 @@ class PurgeJobTest { * Closes tab for job in jobs panel. */ private fun closeTabInJobsPanel(jobName: String, remoteRobot: RemoteRobot) = with(remoteRobot) { - ideFrameImpl(projectName, fixtureStack) { + ideFrameImpl(PROJECT_NAME, fixtureStack) { find(byXpath("//div[@class='TabPanel'][.//div[@text='Jobs:']]//div[@class='ContentTabLabel']")).findText( "//'$datasetName($jobName)'" ).click() @@ -259,7 +259,7 @@ class PurgeJobTest { private fun createJobs(testInfo: TestInfo, remoteRobot: RemoteRobot) = with(remoteRobot) { var n = 1 filesList.forEach { - openLocalFileAndCopyContent(filePath + it, projectName, fixtureStack, remoteRobot) + openLocalFileAndCopyContent(filePath + it, fixtureStack, remoteRobot) Thread.sleep(3000) createMemberAndPasteContentWithMock(testInfo, datasetName, "TEST$n", it, remoteRobot) n++ @@ -270,7 +270,7 @@ class PurgeJobTest { * Creates working set. */ private fun createWS(remoteRobot: RemoteRobot) = with(remoteRobot) { - ideFrameImpl(projectName, fixtureStack) { + ideFrameImpl(PROJECT_NAME, fixtureStack) { createWSFromContextMenu(fixtureStack, closableFixtureCollector) addWorkingSetDialog(fixtureStack) { addWorkingSet(wsName, connectionName) @@ -290,7 +290,7 @@ class PurgeJobTest { * Purges job via action button. */ private fun purgeJobFromTerminal(remoteRobot: RemoteRobot) = with(remoteRobot) { - ideFrameImpl(projectName, fixtureStack) { + ideFrameImpl(PROJECT_NAME, fixtureStack) { clickActionButton(byXpath("//div[@class='ActionButton' and @myaction='Purge Job ()']")) } } @@ -344,14 +344,14 @@ class PurgeJobTest { Pair("port", mockServer.port.toString()), Pair("jobName", jobName), Pair("retCode", rc), Pair("jobStatus", "")))).setResponseCode(200) } ) - ideFrameImpl(projectName, fixtureStack) { + ideFrameImpl(PROJECT_NAME, fixtureStack) { explorer { jesExplorer.click() } } - openJobFilterInExplorer(Triple(jobName, ZOS_USERID, ""), "", projectName, fixtureStack, remoteRobot) + openJobFilterInExplorer(Triple(jobName, ZOS_USERID, ""), "", fixtureStack, remoteRobot) isFirstRequest = false - ideFrameImpl(projectName, fixtureStack) { + ideFrameImpl(PROJECT_NAME, fixtureStack) { explorer { find(viewTree).findAllText { it.text.startsWith("$jobName ($jobId)") }.first() .rightClick() @@ -367,7 +367,7 @@ class PurgeJobTest { */ private fun getJobIdFromPanel(remoteRobot: RemoteRobot): String = with(remoteRobot) { var jobId = "" - ideFrameImpl(projectName, fixtureStack) { + ideFrameImpl(PROJECT_NAME, fixtureStack) { jobId = find(byXpath("//div[@class='Tree']")).findAllText()[2].text.trim() } return jobId @@ -377,7 +377,7 @@ class PurgeJobTest { * Checks notification that correct info is returned for submitted job. */ private fun checkNotificationJobSubmitted(jobName: String, remoteRobot: RemoteRobot) = with(remoteRobot) { - ideFrameImpl(projectName, fixtureStack) { + ideFrameImpl(PROJECT_NAME, fixtureStack) { find(byXpath("//div[@javaclass='javax.swing.JLabel']")).findText("Job $jobName has been submitted") .click() find(byXpath("//div[@tooltiptext.key='tooltip.close.notification']")).click() @@ -398,7 +398,7 @@ class PurgeJobTest { } else { "Error purging $jobName: $jobId" } - ideFrameImpl(projectName, fixtureStack) { + ideFrameImpl(PROJECT_NAME, fixtureStack) { find(byXpath("//div[@javaclass='javax.swing.JLabel']")).findText(textToFind) .click() find(byXpath("//div[@tooltiptext.key='tooltip.close.notification']")).click() @@ -457,9 +457,9 @@ class PurgeJobTest { { MockResponse().setBody("") } ) - createEmptyDatasetMember(datasetName, memberName, projectName, fixtureStack, remoteRobot) + createEmptyDatasetMember(datasetName, memberName, fixtureStack, remoteRobot) isFirstRequest = false - pasteContent(memberName, projectName, fixtureStack, remoteRobot) + pasteContent(memberName, fixtureStack, remoteRobot) Thread.sleep(3000) } @@ -517,7 +517,7 @@ class PurgeJobTest { Pair("port", mockServer.port.toString()), Pair("jobName", jobName), Pair("retCode", rc), Pair("jobStatus", "OUTPUT")))) } ) - submitJob(jobName, projectName, fixtureStack, remoteRobot) + submitJob(jobName, fixtureStack, remoteRobot) } private fun setBodyJobSubmit(jobName: String): String { diff --git a/src/uiTest/kotlin/jes/SubmitJobTest.kt b/src/uiTest/kotlin/jes/SubmitJobTest.kt index f88b291a8..468ea1554 100644 --- a/src/uiTest/kotlin/jes/SubmitJobTest.kt +++ b/src/uiTest/kotlin/jes/SubmitJobTest.kt @@ -21,6 +21,7 @@ import com.intellij.remoterobot.utils.keyboard import okhttp3.mockwebserver.MockResponse import org.junit.jupiter.api.* import org.junit.jupiter.api.extension.ExtendWith +import workingset.PROJECT_NAME import java.awt.event.KeyEvent import java.io.File @@ -36,7 +37,6 @@ class SubmitJobTest { private var wantToClose = mutableListOf( "Allocate Dataset Dialog" ) - private val projectName = "untitled" private val connectionName = "valid connection" private val wsName = "WS1" @@ -51,16 +51,15 @@ class SubmitJobTest { @BeforeAll fun setUpAll(testInfo: TestInfo, remoteRobot: RemoteRobot) { startMockServer() - setUpTestEnvironment(projectName, fixtureStack, closableFixtureCollector, remoteRobot) + setUpTestEnvironment(fixtureStack, closableFixtureCollector, remoteRobot) createValidConnectionWithMock( testInfo, connectionName, - projectName, fixtureStack, closableFixtureCollector, remoteRobot ) - createWsWithoutMask(projectName, wsName, connectionName, fixtureStack, closableFixtureCollector, remoteRobot) + createWsWithoutMask(wsName, connectionName, fixtureStack, closableFixtureCollector, remoteRobot) responseDispatcher.injectEndpoint( "${testInfo.displayName}_${datasetName}_restfiles", { it?.requestLine?.contains("POST /zosmf/restfiles/ds/${datasetName}") ?: false }, @@ -69,7 +68,6 @@ class SubmitJobTest { allocatePDSAndCreateMask( wsName, datasetName, - projectName, fixtureStack, closableFixtureCollector, remoteRobot, @@ -99,10 +97,10 @@ class SubmitJobTest { { it?.requestLine?.contains("DELETE /zosmf/restfiles/ds/${datasetName.uppercase()}") ?: false }, { MockResponse().setBody("{}") } ) - deleteDataset(datasetName, projectName, fixtureStack, remoteRobot) + deleteDataset(datasetName, fixtureStack, remoteRobot) mockServer.shutdown() - clearEnvironment(projectName, fixtureStack, closableFixtureCollector, remoteRobot) - ideFrameImpl(projectName, fixtureStack) { + clearEnvironment(fixtureStack, closableFixtureCollector, remoteRobot) + ideFrameImpl(PROJECT_NAME, fixtureStack) { close() } } @@ -170,7 +168,7 @@ class SubmitJobTest { Pair("port", mockServer.port.toString()), Pair("jobName", jobName), Pair("retCode", "CC 0000"), Pair("jobStatus", "OUTPUT")))).setResponseCode(200) } ) - ideFrameImpl(projectName, fixtureStack) { + ideFrameImpl(PROJECT_NAME, fixtureStack) { explorer { jesExplorer.click() settings(closableFixtureCollector, fixtureStack) @@ -204,7 +202,7 @@ class SubmitJobTest { { it?.requestLine?.contains("GET /zosmf/restjobs/jobs/${jobName}/JOB07380/files/") ?: false }, { MockResponse().setBody("").setResponseCode(200) } ) - ideFrameImpl(projectName, fixtureStack) { + ideFrameImpl(PROJECT_NAME, fixtureStack) { if (find(viewTree).findAllText { it.text.startsWith(jobName) }.size > 1) { find(viewTree).findAllText { it.text.startsWith(jobName) }.first().doubleClick() } @@ -233,7 +231,7 @@ class SubmitJobTest { remoteRobot: RemoteRobot ) = with(remoteRobot) { - openLocalFileAndCopyContent(filePath + fileName, projectName, fixtureStack, remoteRobot) + openLocalFileAndCopyContent(filePath + fileName, fixtureStack, remoteRobot) Thread.sleep(3000) createMemberAndPasteContentWithMock(testInfo, datasetName, jobName, fileName, remoteRobot) submitJobWithMock(testInfo, datasetName, jobName, rc, remoteRobot) @@ -290,7 +288,7 @@ class SubmitJobTest { Pair("port", mockServer.port.toString()), Pair("jobName", jobName), Pair("retCode", rc), Pair("jobStatus", "OUTPUT")))) } ) - submitJob(jobName, projectName, fixtureStack, remoteRobot) + submitJob(jobName, fixtureStack, remoteRobot) } private fun createMemberAndPasteContentWithMock( @@ -320,9 +318,9 @@ class SubmitJobTest { { MockResponse().setBody("") } ) - createEmptyDatasetMember(datasetName, memberName, projectName, fixtureStack, remoteRobot) + createEmptyDatasetMember(datasetName, memberName, fixtureStack, remoteRobot) isFirstRequest = false - pasteContent(memberName, projectName, fixtureStack, remoteRobot) + pasteContent(memberName, fixtureStack, remoteRobot) } @@ -331,7 +329,7 @@ class SubmitJobTest { */ private fun checkTabPanelAndConsole(jobName: String, jobId: String, rc: String, remoteRobot: RemoteRobot) = with(remoteRobot) { - ideFrameImpl(projectName, fixtureStack) { + ideFrameImpl(PROJECT_NAME, fixtureStack) { find(byXpath("//div[contains(@accessiblename.key, 'editor.accessible.name')]")).findText( "JOB $jobName($jobId) EXECUTED" ) @@ -355,7 +353,7 @@ class SubmitJobTest { */ private fun getJobIdFromPanel(remoteRobot: RemoteRobot): String = with(remoteRobot) { var jobId = "" - ideFrameImpl(projectName, fixtureStack) { + ideFrameImpl(PROJECT_NAME, fixtureStack) { jobId = find(byXpath("//div[@class='Tree']")).findAllText()[2].text.trim() } return jobId @@ -365,7 +363,7 @@ class SubmitJobTest { * Checks notification that correct info is returned. */ private fun checkNotification(jobName: String, remoteRobot: RemoteRobot) = with(remoteRobot) { - ideFrameImpl(projectName, fixtureStack) { + ideFrameImpl(PROJECT_NAME, fixtureStack) { find(byXpath("//div[@javaclass='javax.swing.JLabel']")).findText("Job $jobName has been submitted") .click() find(byXpath("//div[@tooltiptext.key='tooltip.close.notification']")).click() @@ -413,6 +411,6 @@ class SubmitJobTest { MockResponse().setBody(buildListMembersJson()) } ) - openOrCloseWorkingSetInExplorer(wsName, projectName, fixtureStack, remoteRobot) + openOrCloseWorkingSetInExplorer(wsName, fixtureStack, remoteRobot) } } \ No newline at end of file diff --git a/src/uiTest/kotlin/jes/ViewJwsPropertyTest.kt b/src/uiTest/kotlin/jes/ViewJwsPropertyTest.kt index 5d9db6368..1c62a0939 100644 --- a/src/uiTest/kotlin/jes/ViewJwsPropertyTest.kt +++ b/src/uiTest/kotlin/jes/ViewJwsPropertyTest.kt @@ -22,6 +22,8 @@ import com.intellij.remoterobot.search.locators.byXpath import okhttp3.mockwebserver.MockResponse import org.junit.jupiter.api.* import org.junit.jupiter.api.extension.ExtendWith +import workingset.EMPTY_DATASET_MESSAGE +import workingset.PROJECT_NAME /** * Tests viewing job and spool file properties. @@ -33,7 +35,6 @@ class ViewJwsPropertyTest { private val closableFixtureCollector = ClosableFixtureCollector() private val fixtureStack = mutableListOf() - private val projectName = "untitled" private val connectionName = "con1" private val jwsName = "JWS name" private val jobsFilterName = Triple("*", ZOS_USERID.uppercase(), "") @@ -56,9 +57,8 @@ class ViewJwsPropertyTest { { it?.requestLine?.contains("zosmf/resttopology/systems") ?: false }, { MockResponse().setBody(responseDispatcher.readMockJson("infoResponse") ?: "") } ) - setUpTestEnvironment(projectName, fixtureStack, closableFixtureCollector, remoteRobot) + setUpTestEnvironment(fixtureStack, closableFixtureCollector, remoteRobot) createConnection( - projectName, fixtureStack, closableFixtureCollector, connectionName, @@ -66,7 +66,7 @@ class ViewJwsPropertyTest { remoteRobot, "https://${mockServer.hostName}:${mockServer.port}" ) - ideFrameImpl(projectName, fixtureStack) { + ideFrameImpl(PROJECT_NAME, fixtureStack) { createJWSFromContextMenu(fixtureStack, closableFixtureCollector) addJesWorkingSetDialog(fixtureStack) { addJesWorkingSet(jwsName, connectionName) @@ -94,7 +94,7 @@ class ViewJwsPropertyTest { "listSpoolFiles_restfiles", { it?.requestLine?.contains("GET /zosmf/restjobs/jobs/") ?: false }, { MockResponse().setBody(replaceInJson("getSingleSpoolFile", mapOf(Pair("jobName", jobName), Pair("spoolFileName", spoolFileName)))) }) - ideFrameImpl(projectName, fixtureStack) { + ideFrameImpl(PROJECT_NAME, fixtureStack) { explorer { jesExplorer.click() find(viewTree).findText(jwsName).doubleClick() @@ -110,8 +110,8 @@ class ViewJwsPropertyTest { fun tearDownAll(remoteRobot: RemoteRobot) = with(remoteRobot) { mockServer.shutdown() - clearEnvironment(projectName, fixtureStack, closableFixtureCollector, remoteRobot) - ideFrameImpl(projectName, fixtureStack) { + clearEnvironment(fixtureStack, closableFixtureCollector, remoteRobot) + ideFrameImpl(PROJECT_NAME, fixtureStack) { close() } } @@ -122,7 +122,7 @@ class ViewJwsPropertyTest { @Test @Order(1) fun testViewJobProperties(remoteRobot: RemoteRobot) = with(remoteRobot) { - ideFrameImpl(projectName, fixtureStack) { + ideFrameImpl(PROJECT_NAME, fixtureStack) { explorer { find(viewTree).findAllText{ it.text.startsWith("$jobName (JOB07380)") }.first() .rightClick() @@ -146,7 +146,7 @@ class ViewJwsPropertyTest { @Test @Order(2) fun testViewSpoolFileProperties(remoteRobot: RemoteRobot) = with(remoteRobot) { - ideFrameImpl(projectName, fixtureStack) { + ideFrameImpl(PROJECT_NAME, fixtureStack) { explorer { jesExplorer.click() Thread.sleep(1000) diff --git a/src/uiTest/kotlin/settings/connection/ConnectionManager.kt b/src/uiTest/kotlin/settings/connection/ConnectionManager.kt index cc69a5cf6..084aebce9 100644 --- a/src/uiTest/kotlin/settings/connection/ConnectionManager.kt +++ b/src/uiTest/kotlin/settings/connection/ConnectionManager.kt @@ -23,6 +23,7 @@ import org.junit.jupiter.api.* import org.junit.jupiter.api.Assertions.assertFalse import org.junit.jupiter.api.Assertions.assertTrue import org.junit.jupiter.api.extension.ExtendWith +import workingset.PROJECT_NAME import java.time.Duration /** @@ -47,7 +48,7 @@ class ConnectionManager { @BeforeAll fun setUpAll(remoteRobot: RemoteRobot) { startMockServer() - setUpTestEnvironment(projectName, fixtureStack, closableFixtureCollector, remoteRobot) + setUpTestEnvironment(fixtureStack, closableFixtureCollector, remoteRobot) } /** @@ -57,8 +58,8 @@ class ConnectionManager { fun tearDownAll(remoteRobot: RemoteRobot) = with(remoteRobot) { mockServer.shutdown() - clearEnvironment(projectName, fixtureStack, closableFixtureCollector, remoteRobot) - ideFrameImpl(projectName, fixtureStack) { + clearEnvironment(fixtureStack, closableFixtureCollector, remoteRobot) + ideFrameImpl(PROJECT_NAME, fixtureStack) { close() } } @@ -80,7 +81,7 @@ class ConnectionManager { fun testAddWrongConnection(remoteRobot: RemoteRobot) = with(remoteRobot) { val host = "a.com" - ideFrameImpl(projectName, fixtureStack) { + ideFrameImpl(PROJECT_NAME, fixtureStack) { explorer { settings(closableFixtureCollector, fixtureStack) } @@ -108,7 +109,7 @@ class ConnectionManager { @Order(2) fun testAddTwoConnectionsWithTheSameName(remoteRobot: RemoteRobot) = with(remoteRobot) { val connectionName = "a" - ideFrameImpl(projectName, fixtureStack) { + ideFrameImpl(PROJECT_NAME, fixtureStack) { explorer { settings(closableFixtureCollector, fixtureStack) } @@ -164,7 +165,6 @@ class ConnectionManager { { MockResponse().setBody(responseDispatcher.readMockJson("infoResponse") ?: "") } ) createConnection( - projectName, fixtureStack, closableFixtureCollector, "valid connection1", @@ -190,7 +190,7 @@ class ConnectionManager { { it?.requestLine?.contains("zosmf/resttopology/systems") ?: false }, { MockResponse().setBody(responseDispatcher.readMockJson("infoResponse") ?: "") } ) - ideFrameImpl(projectName, fixtureStack) { + ideFrameImpl(PROJECT_NAME, fixtureStack) { explorer { settings(closableFixtureCollector, fixtureStack) } @@ -228,7 +228,7 @@ class ConnectionManager { { it?.requestLine?.contains("zosmf/info") ?: false }, { MockResponse().setResponseCode(401) } ) - ideFrameImpl(projectName, fixtureStack) { + ideFrameImpl(PROJECT_NAME, fixtureStack) { explorer { settings(closableFixtureCollector, fixtureStack) } @@ -270,7 +270,7 @@ class ConnectionManager { { it?.requestLine?.contains("zosmf/info") ?: false }, { MockResponse().setBody("Unable to find valid certification path to requested target") } ) - ideFrameImpl(projectName, fixtureStack) { + ideFrameImpl(PROJECT_NAME, fixtureStack) { explorer { settings(closableFixtureCollector, fixtureStack) } @@ -318,7 +318,6 @@ class ConnectionManager { { MockResponse().setBody(responseDispatcher.readMockJson("infoResponse") ?: "") } ) createConnection( - projectName, fixtureStack, closableFixtureCollector, "A".repeat(200), @@ -339,7 +338,7 @@ class ConnectionManager { { it?.requestLine?.contains("zosmf/info") ?: false }, { MockResponse().setBody("Please provide a valid URL to z/OSMF. Example: https://myhost.com:10443") } ) - ideFrameImpl(projectName, fixtureStack) { + ideFrameImpl(PROJECT_NAME, fixtureStack) { explorer { settings(closableFixtureCollector, fixtureStack) } @@ -375,7 +374,7 @@ class ConnectionManager { @Order(9) fun testEditConnectionFromValidToInvalid(remoteRobot: RemoteRobot) = with(remoteRobot) { val testPort = "104431" - ideFrameImpl(projectName, fixtureStack) { + ideFrameImpl(PROJECT_NAME, fixtureStack) { explorer { settings(closableFixtureCollector, fixtureStack) } @@ -424,7 +423,7 @@ class ConnectionManager { { it?.requestLine?.contains("zosmf/resttopology/systems") ?: false }, { MockResponse().setBody(responseDispatcher.readMockJson("infoResponse") ?: "") } ) - ideFrameImpl(projectName, fixtureStack) { + ideFrameImpl(PROJECT_NAME, fixtureStack) { explorer { settings(closableFixtureCollector, fixtureStack) } @@ -474,7 +473,7 @@ class ConnectionManager { { it?.requestLine?.contains("zosmf/resttopology/systems") ?: false }, { MockResponse().setBody(responseDispatcher.readMockJson("infoResponse") ?: "") } ) - ideFrameImpl(projectName, fixtureStack) { + ideFrameImpl(PROJECT_NAME, fixtureStack) { explorer { settings(closableFixtureCollector, fixtureStack) } @@ -564,7 +563,6 @@ class ConnectionManager { { MockResponse().setBody(responseDispatcher.readMockJson("infoResponse") ?: "") } ) createConnection( - projectName, fixtureStack, closableFixtureCollector, connectionName, @@ -583,7 +581,7 @@ class ConnectionManager { */ private fun deleteConnection(connectionName: String, wsName: String, jwsName: String, remoteRobot: RemoteRobot) = with(remoteRobot) { - ideFrameImpl(projectName, fixtureStack) { + ideFrameImpl(PROJECT_NAME, fixtureStack) { explorer { settings(closableFixtureCollector, fixtureStack) } @@ -631,7 +629,7 @@ class ConnectionManager { { it?.requestLine?.contains("/zosmf/restjobs/jobs") ?: false }, { MockResponse().setBody("[]") } ) - ideFrameImpl(projectName, fixtureStack) { + ideFrameImpl(PROJECT_NAME, fixtureStack) { explorer { settings(closableFixtureCollector, fixtureStack) } @@ -661,7 +659,7 @@ class ConnectionManager { { it?.requestLine?.contains("zosmf/restfiles/ds?dslevel=") ?: false }, { MockResponse().setBody("{}") } ) - ideFrameImpl(projectName, fixtureStack) { + ideFrameImpl(PROJECT_NAME, fixtureStack) { explorer { settings(closableFixtureCollector, fixtureStack) } diff --git a/src/uiTest/kotlin/testutils/InjectDispatcher.kt.kt b/src/uiTest/kotlin/testutils/InjectDispatcher.kt.kt new file mode 100644 index 000000000..f3e0f9701 --- /dev/null +++ b/src/uiTest/kotlin/testutils/InjectDispatcher.kt.kt @@ -0,0 +1,209 @@ +/* + * This program and the accompanying materials are made available under the terms of the + * Eclipse Public License v2.0 which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-v20.html + * + * SPDX-License-Identifier: EPL-2.0 + * + * Copyright IBA Group 2020 + */ +package workingset.testutils + +import okhttp3.mockwebserver.MockResponse +import auxiliary.* +import org.junit.jupiter.api.TestInfo +import workingset.DATA_SET_RENAME_FAILED + +//import testutils.MockResponseDispatcher + +fun injectRenameDataset(testInfo: TestInfo, dsFinalName: String, dsName: String){ + responseDispatcher.injectEndpoint( + "${testInfo.displayName}_restfiles_rename_ds", + { it?.requestLine?.contains("PUT /zosmf/restfiles/ds/${dsFinalName}") ?: false }, + { MockResponse().setBody("{\"request\":\"rename\",\"from-dataset\":{\"dsn\":\"${dsName}\"}}") } + ) +} + +fun injectRenameMember(testInfo: TestInfo, datasetName: String, memberNameNew: String, memberNameOld: String, handler: Boolean=false){ + responseDispatcher.injectEndpoint( + "${testInfo.displayName}_restfiles_rename_member", + { it?.requestLine?.contains("PUT /zosmf/restfiles/ds/${datasetName}(${memberNameNew})") ?: handler }, + { MockResponse().setBody("{\"request\":\"rename\",\"from-dataset\":{\"dsn\":\"${datasetName}\",\"member\":\"${memberNameOld}\"}}") } + ) +} + +fun injectRenameMemberUnsuccessful(testInfo: TestInfo, datasetName: String, memberNameNew: String, memberNameOld: String, code: Int, rcCode: String, errorDetail:String, handler: Boolean=false){ + responseDispatcher.injectEndpoint( + "${testInfo.displayName}_restfiles_rename_member", + { it?.requestLine?.contains("PUT /zosmf/restfiles/ds/${datasetName}(${memberNameOld})") ?: handler }, + { + MockResponse().setBody("{\"request\":\"rename\",\"from-dataset\":{\"dsn\":\"${datasetName}\",\"member\":\"${memberNameNew}\"}}") + .setResponseCode(code) + .setBody("{\"category\":\"4.0\",\"message\":\"Rename member failed\",\"rc\":$rcCode,\"details\":[\"ISRZ002 $errorDetail - Directory already contains the specified member name.\"],\"reason\":\"0.0\"}") + } + ) +} + +fun injectRenameDatasetUnsuccessful(testInfo: TestInfo, dsFinalName: String, anotherDsName: String, code: Int, rcCode: String, errorDetail:String, handler: Boolean=false){ + responseDispatcher.injectEndpoint( + "${testInfo.displayName}_restfiles_rename_ds", + { it?.requestLine?.contains("PUT /zosmf/restfiles/ds/${anotherDsName}") ?: handler }, + { + MockResponse().setBody("{\"request\":\"rename\",\"from-dataset\":{\"dsn\":\"${dsFinalName}\"}}") + .setResponseCode(code) + .setBody("{\"category\":\"1.0\",\"message\":\"$DATA_SET_RENAME_FAILED\",\"rc\":\"$rcCode\",\"details\":[\"$errorDetail\"],\"reason\":\"6.0\"}") + } + ) +} + + +fun injectMemberList(testInfo: TestInfo, datasetName: String, listElem: List, listName: String? = null, isFirstRequest: Boolean= true){//= true){ + val requestName = if (listName != null) { + "${testInfo.displayName}_restfiles_listmembers$listName" + } else { + "${testInfo.displayName}_restfiles_listmembers" + } + + val body_headr = "{\"items\":[" + val body_tell = "],\"returnedRows\": ${listElem.size},\"JSONversion\": 1}" + val memebersString = listElem.joinToString(separator = ",") { "{\"member\": \"$it\"}" } + + responseDispatcher.injectEndpoint( + requestName, + { it?.requestLine?.contains("GET /zosmf/restfiles/ds/${datasetName}/member") ?: false && isFirstRequest}, + { MockResponse().setBody(body_headr + memebersString + body_tell)} + ) +} +fun injectSingleMember(testInfo:TestInfo, datasetName: String, listMembersInDataset: MutableList){ + responseDispatcher.injectEndpoint( + "${testInfo.displayName}_restfiles_listmembers", + { it?.requestLine?.contains("GET /zosmf/restfiles/ds/${datasetName}/member") ?: false }, + { + MockResponse().setBody(buildListMembersJson(listMembersInDataset)) + } + ) +} +fun injectSingleSpecificMember(pdsName: String, memberName: String, responseCode: Int=204){ + responseDispatcher.injectEndpoint( + "allocateDatasetMember_restfiles", + { it?.requestLine?.contains("PUT /zosmf/restfiles/ds/$pdsName($memberName)") ?: false }, + { MockResponse().setResponseCode(responseCode) } + ) +} + +fun injectListAllAllocatedDatasets(datasetMask: String, mapListDatasets: MutableMap, handler: Boolean = false){ + responseDispatcher.injectEndpoint( + "listAllAllocatedDatasets_restfiles", + { it?.requestLine?.contains("GET /zosmf/restfiles/ds?dslevel=${datasetMask}*") ?: handler }, + { MockResponse().setBody(buildFinalListDatasetJson(mapListDatasets)) } + ) +} + +fun injectListEmptyData(testInfo:TestInfo, isDataset: Boolean=true, handler: Boolean = false){ + val pathType = if (isDataset) "ds?dslevel=" else "fs?path" + responseDispatcher.injectEndpoint( + "${testInfo.displayName}_restfiles", + { it?.requestLine?.contains("GET /zosmf/restfiles/${pathType}") ?: handler }, + { MockResponse().setBody("{}") } + ) +} + +fun injectListAllAllocatedDatasetsWithContents(testInfo:TestInfo,datasetName: String, mapListDatasets: MutableMap, handler: Boolean = false){ + responseDispatcher.injectEndpoint( + "${testInfo.displayName}_restfiles", + { + it?.requestLine?.contains("GET /zosmf/restfiles/ds?dslevel=${datasetName}") + ?: false + }, + { MockResponse().setBody(buildResponseListJson(mapListDatasets, handler)) } + ) +} + +fun injectMemberContent(testInfo: TestInfo,datasetName:String, memberName:String, memberContent: String="", handler: Boolean = false){ + responseDispatcher.injectEndpoint( + "${testInfo.displayName}_restfiles_getmember", + { it?.requestLine?.contains("GET /zosmf/restfiles/ds/${datasetName}($memberName)") ?: handler }, + { MockResponse().setBody(memberContent) } + ) +} + +fun injectPsDatasetContent(testInfo: TestInfo, datasetName:String, psContent:String="", handler: Boolean = false){ + responseDispatcher.injectEndpoint( + "${testInfo.displayName}_restfiles_getcontent", + { it?.requestLine?.contains("GET /zosmf/restfiles/ds/-(TESTVOL)/$datasetName") ?: handler }, + { MockResponse().setBody(psContent) } + ) +} + +fun injectAllocateUssFile(ussMaskName:String, ussFileName:String, responseCode: Int = 201, handler: Boolean = false){ + responseDispatcher.injectEndpoint( + "allocateUssFile_restfiles", + { it?.requestLine?.contains("POST /zosmf/restfiles/fs$ussMaskName/$ussFileName") ?: handler }, + { MockResponse().setResponseCode(responseCode) } + ) +} +fun injectListAllUssFiles(mapListUssFiles: Map, handler: Boolean = false){ + responseDispatcher.injectEndpoint( + "listAllUssFiles_restfiles", + { it?.requestLine?.contains("GET /zosmf/restfiles/fs?path") ?: handler }, + { MockResponse().setBody(buildResponseListJson(mapListUssFiles, true)) } + ) +} + +fun injectTestInfoRestTopology(testInfo: TestInfo, handler: Boolean = false){ + responseDispatcher.injectEndpoint( + "${testInfo.displayName}_resttopology", + { it?.requestLine?.contains("zosmf/resttopology/systems") ?: handler }, + { MockResponse().setBody(responseDispatcher.readMockJson("infoResponse") ?: "") } + ) +} + + +fun injectTestInfo(testInfo: TestInfo,handler: Boolean = false){ + responseDispatcher.injectEndpoint( + "${testInfo.displayName}_info", + { it?.requestLine?.contains("zosmf/info") ?: handler }, + { MockResponse().setBody(responseDispatcher.readMockJson("infoResponse") ?: "") } + ) +} + +fun injectInvalidUrlPortInfo(testInfo: TestInfo,testPort:String, handler: Boolean = false){ + responseDispatcher.injectEndpoint( + "${testInfo.displayName}_info", + { it?.requestLine?.contains("zosmf/info") ?: handler }, + { MockResponse().setBody("Invalid URL port: \"${testPort}\"") } + ) +} + +fun injectEmptyZosmfRestfilesPath(testInfo: TestInfo, handler: Boolean = false){ + responseDispatcher.injectEndpoint( + "${testInfo.displayName}_second", + { it?.requestLine?.contains("zosmf/restfiles/") ?: handler }, + { MockResponse().setBody("{}") } + ) +} +//class InjectDispatcher: MockResponseDispatcher() { +// +// +// fun injectAllocationResultPo( +// recordFormatShort: String, +// dsName:String, +// dsOrganisationShort:String, +// recordLength:Int, +// handler: Boolean = false){ +// +// responseDispatcher.injectEndpoint( +// "testAllocateValid${dsOrganisationShort}Datasets_${recordFormatShort}_restfiles", +// { it?.requestLine?.contains("POST /zosmf/restfiles/ds/${dsName}") ?: handler }, +// { MockResponse().setBody("{\"dsorg\":\"${dsOrganisationShort}\",\"alcunit\":\"TRK\",\"primary\":10,\"secondary\":1,\"dirblk\":1,\"recfm\":\"${recordFormatShort}\",\"blksize\":3200,\"lrecl\":${recordLength}}") } +// ) +// +// } +// fun injectRenameMember(testInfo: TestInfo, datasetName: String, memberNameNew: String, memberNameOld: String, handler: Boolean=false){ +// responseDispatcher.injectEndpoint( +// "${testInfo.displayName}_restfiles_rename_member", +// { it?.requestLine?.contains("PUT /zosmf/restfiles/ds/${datasetName}(${memberNameNew})") ?: false }, +// { MockResponse().setBody("{\"request\":\"rename\",\"from-dataset\":{\"dsn\":\"${datasetName}\",\"member\":\"${memberNameOld}\"}}") } +// ) +// } +//} \ No newline at end of file diff --git a/src/uiTest/kotlin/testutils/MockResponseDispatcher.kt b/src/uiTest/kotlin/testutils/MockResponseDispatcher.kt index 797c0c4df..027b31d8b 100644 --- a/src/uiTest/kotlin/testutils/MockResponseDispatcher.kt +++ b/src/uiTest/kotlin/testutils/MockResponseDispatcher.kt @@ -9,10 +9,13 @@ */ package testutils - +//import auxiliary.buildFinalListDatasetJson +import auxiliary.responseDispatcher import okhttp3.mockwebserver.Dispatcher import okhttp3.mockwebserver.MockResponse import okhttp3.mockwebserver.RecordedRequest +import org.junit.jupiter.api.TestInfo +import java.io.File data class ValidationListElem( val name: String, @@ -20,7 +23,7 @@ data class ValidationListElem( val handler: (RecordedRequest?) -> MockResponse ) -class MockResponseDispatcher : Dispatcher() { +open class MockResponseDispatcher : Dispatcher() { private var validationList = mutableListOf() @@ -48,11 +51,153 @@ class MockResponseDispatcher : Dispatcher() { validationList.clear() } + fun injectAllocationResultPo( + datasetOrganization: String, + recordFormatShort: String, + dsName:String, + dsOrganisationShort:String, + recordLength:Int, + handler: Boolean = false){ + + responseDispatcher.injectEndpoint( + "testAllocateValid${datasetOrganization}Datasets_${recordFormatShort}_restfiles", + { it?.requestLine?.contains("POST /zosmf/restfiles/ds/${dsName}") ?: handler }, + { MockResponse().setBody("{\"dsorg\":\"${dsOrganisationShort}\",\"alcunit\":\"TRK\",\"primary\":10,\"secondary\":1,\"dirblk\":1,\"recfm\":\"${recordFormatShort}\",\"blksize\":3200,\"lrecl\":${recordLength}}") } + ) + + } + + fun injectAllocationResultPds(pdsName: String, handler: Boolean = false){ + + responseDispatcher.injectEndpoint( + "allocatePds_restfiles", + { it?.requestLine?.contains("POST /zosmf/restfiles/ds/$pdsName") ?: handler }, + { MockResponse().setBody("{\"dsorg\":\"PDS\",\"alcunit\":\"TRK\",\"primary\":10,\"secondary\":1,\"dirblk\":2,\"recfm\":\"VB\",\"blksize\":6120,\"lrecl\":255}") } + ) + + } + + fun injectAllocatedDatasets(datasetMask: String, body: String, datasetName:String, handler: Boolean = false){ + responseDispatcher.injectEndpoint( + "listAllocatedDatasets_restfiles_${datasetMask}", + { it?.requestLine?.contains("GET /zosmf/restfiles/ds?dslevel=${datasetName}*") ?: handler }, + { MockResponse().setBody(body) } + ) + } + fun injectTestInfoForPdsDataset(testInfoDisplayedName: String, body: String, pdsName:String, handler: Boolean = false){ + responseDispatcher.injectEndpoint( + "${testInfoDisplayedName}_${pdsName}_restfiles", + { it?.requestLine?.contains("POST /zosmf/restfiles/ds/${pdsName}") ?: handler }, + { MockResponse().setBody(body) } + ) + } + + fun injectListMembers(body: String, handler: Boolean = false){ + responseDispatcher.injectEndpoint( + "listMembers_restfiles", + { it?.requestLine?.contains("/member") ?: handler }, + { MockResponse().setBody(body) } + ) + } + + fun injectListAllDatasetMembersRestfiles(pdsName: String, body: String, handler: Boolean = false){ + responseDispatcher.injectEndpoint( + "listAllDatasetMembers_restfiles", + { it?.requestLine?.contains("GET /zosmf/restfiles/ds/$pdsName/member") ?: handler }, + { MockResponse().setBody(body) } + ) + } + + fun injectDeleteDataset(datasetName: String, handler: Boolean = false){ + responseDispatcher.injectEndpoint( + "deleteDataset_restfiles_${datasetName}", + { it?.requestLine?.contains("DELETE /zosmf/restfiles/ds/${datasetName}") ?: handler }, + { MockResponse().setBody("{}") } + ) + } + + fun injectTestInfo(testInfo: TestInfo,handler: Boolean = false){ + responseDispatcher.injectEndpoint( + "${testInfo.displayName}_info", + { it?.requestLine?.contains("zosmf/info") ?: handler }, + { MockResponse().setBody(responseDispatcher.readMockJson("infoResponse") ?: "") } + ) + } + + fun injectTestInfoRestTopology(testInfo: TestInfo, handler: Boolean = false){ + responseDispatcher.injectEndpoint( + "${testInfo.displayName}_resttopology", + { it?.requestLine?.contains("zosmf/resttopology/systems") ?: handler }, + { MockResponse().setBody(responseDispatcher.readMockJson("infoResponse") ?: "") } + ) + } + + fun injectMigratePdsRestFiles(pdsName:String, contains: String, handler: Boolean = false){ + responseDispatcher.injectEndpoint( + "migratePds_restfiles", + { it?.requestLine?.contains("PUT /zosmf/restfiles/ds/$pdsName") ?: false && + it?.body?.toString()?.contains(contains) ?: handler }, + { MockResponse().setResponseCode(200) } + ) + } + + fun injectRecallPds(pdsName: String, handler: Boolean = false){ + responseDispatcher.injectEndpoint( + "migratePds_restfiles", + { it?.requestLine?.contains("PUT /zosmf/restfiles/ds/$pdsName") ?: false && + it?.body?.toString()?.contains("hrecall") ?: handler }, + { MockResponse().setResponseCode(200) } + ) + } + + internal open fun injectRenameMember(testInfo: TestInfo, datasetName: String, memberNameNew: String, memberNameOld: String, handler: Boolean = false){ + responseDispatcher.injectEndpoint( + "${testInfo.displayName}_restfiles_rename_member", + { it?.requestLine?.contains("PUT /zosmf/restfiles/ds/${datasetName}(${memberNameNew})") ?: false }, + { MockResponse().setBody("{\"request\":\"rename\",\"from-dataset\":{\"dsn\":\"${datasetName}\",\"member\":\"${memberNameOld}\"}}") } + ) + } + fun injectMemberList(testInfo: TestInfo, datasetName: String, listElem: List, listName: String? = null){//= true){ + val requestName = if (listName != null) { + "${testInfo.displayName}_restfiles_listmembers$listName" + } else { + "${testInfo.displayName}_restfiles_listmembers" + } + + val body_headr = "{\"items\":[" + val body_tell = "],\"returnedRows\": ${listElem.size},\"JSONversion\": 1}" + val memebersString = listElem.joinToString(separator = ",") { "{\"member\": \"$it\"}" } + + injectEndpoint( + requestName, + { it?.requestLine?.contains("GET /zosmf/restfiles/ds/${datasetName}/member") ?: false}, + { MockResponse().setBody(body_headr + memebersString + body_tell)} + ) + } + + + override fun dispatch(request: RecordedRequest): MockResponse { -// val fileName = System.getProperty("user.dir") + "/src/uiTest/resources/request_received.txt" -// File(fileName).writeText("${request.requestLine}\n${request.requestUrl}\n${request.path}\n${request.body}\n") - return validationList + val fileName = System.getProperty("user.dir") + "/src/uiTest/resources/request_received.txt" + + + + val x = validationList .firstOrNull { it.validator(request) } + +// if (x?.handler != null && x != null){ +// val kClass = x.handler::class +// val properties = kClass.memberProperties +// +// for (property in properties) { +// +// File(fileName).appendText("=================${property.name}===========") +//// println("${property.name}: ${property.get(x.handler)}") +// } +// } + + File(fileName).appendText("${x?.name}\n${x?.validator}${x?.handler}\n${x?.handler}\n") + return x ?.handler ?.let { it(request) } ?: MockResponse().setBody("Response is not implemented").setResponseCode(404) diff --git a/src/uiTest/kotlin/workingset/AllocateDatasetTest.kt b/src/uiTest/kotlin/workingset/AllocateDatasetTest.kt index 3b968801a..eb79ba8ed 100644 --- a/src/uiTest/kotlin/workingset/AllocateDatasetTest.kt +++ b/src/uiTest/kotlin/workingset/AllocateDatasetTest.kt @@ -17,71 +17,72 @@ import auxiliary.components.actionMenuItem import auxiliary.containers.* import com.intellij.remoterobot.RemoteRobot import com.intellij.remoterobot.fixtures.ComponentFixture -import com.intellij.remoterobot.fixtures.ContainerFixture import com.intellij.remoterobot.fixtures.HeavyWeightWindowFixture import com.intellij.remoterobot.search.locators.Locator -import com.intellij.remoterobot.search.locators.byXpath import io.kotest.matchers.string.shouldContain -import okhttp3.mockwebserver.MockResponse import org.junit.jupiter.api.* import org.junit.jupiter.api.Assertions.assertFalse import org.junit.jupiter.api.extension.ExtendWith +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.Arguments +import org.junit.jupiter.params.provider.MethodSource +import org.junit.jupiter.params.provider.ValueSource +import workingset.testutils.injectListAllAllocatedDatasets import java.time.Duration +import java.util.stream.Stream /** * Tests allocating datasets with valid and invalid inputs. */ @TestInstance(TestInstance.Lifecycle.PER_CLASS) @ExtendWith(RemoteRobotExtension::class) -class AllocateDatasetTest { +class AllocateDatasetTest : WorkingSetBase() { private var closableFixtureCollector = ClosableFixtureCollector() private var fixtureStack = mutableListOf() private var wantToClose = mutableListOf( "Allocate Dataset Dialog" ) - private val projectName = "untitled" - private val connectionName = "valid connection" - private val wsName = "WS1" - private val datasetName = "$ZOS_USERID.ALLOC." + override val datasetName = "$ZOS_USERID.ALLOC." + override val wsName = "WS1" + private val datasetMask = "$ZOS_USERID.ALLOC." private val recordFormats = mutableListOf("F", "FB", "V", "VA", "VB") private var datasetsToBeDeleted = mutableListOf() - //TODO change message when ijmp-907 is fixed - private val numberGreaterThanOneMsg = "Enter a number greater than or equal to 1" - private val enterPositiveNumberMsg = "Enter a positive number" + companion object { + @JvmStatic + fun valuesProvider(): Stream { + return invalidAllocateScenarios.entries.stream().map { entry -> + Arguments.of(entry.key, entry.value) + } + } + } + private var mapListDatasets = mutableMapOf() - /** - * Opens the project and Explorer, clears test environment, creates working set and mask. - */ @BeforeAll - fun setUpAll(testInfo: TestInfo, remoteRobot: RemoteRobot) = with(remoteRobot) { + fun setUpAll(testInfo: TestInfo, remoteRobot: RemoteRobot) { startMockServer() - setUpTestEnvironment(projectName, fixtureStack, closableFixtureCollector, remoteRobot) + setUpTestEnvironment(fixtureStack, closableFixtureCollector, remoteRobot) createValidConnectionWithMock( testInfo, connectionName, - projectName, fixtureStack, closableFixtureCollector, remoteRobot ) - createWsAndMask(remoteRobot) - + return createWsAndMask(remoteRobot) } /** * Closes the project and clears test environment, deletes created datasets. */ @AfterAll - fun tearDownAll(remoteRobot: RemoteRobot) = with(remoteRobot) { - deleteDatasets(remoteRobot) + fun tearDownAll(remoteRobot: RemoteRobot) { +// deleteDatasets(remoteRobot) mockServer.shutdown() - clearEnvironment(projectName, fixtureStack, closableFixtureCollector, remoteRobot) - ideFrameImpl(projectName, fixtureStack) { - close() - } + clearEnvironment(fixtureStack, closableFixtureCollector, remoteRobot) + closeIntelligentProject(fixtureStack, remoteRobot) } /** @@ -89,6 +90,7 @@ class AllocateDatasetTest { */ @AfterEach fun tearDown(remoteRobot: RemoteRobot) { + deleteDatasets(remoteRobot) responseDispatcher.removeAllEndpoints() closableFixtureCollector.closeWantedClosables(wantToClose, remoteRobot) } @@ -96,158 +98,66 @@ class AllocateDatasetTest { //todo add tests with advanced parameters when switched to mock /** - * Tests to allocate PO datasets with valid parameters. - */ - @Test - fun testAllocateValidPODatasets(remoteRobot: RemoteRobot) { - doValidTest("PO", remoteRobot) - } - - /** - * Tests to allocate PS datasets with valid parameters. - */ - @Test - fun testAllocateValidPSDatasets(remoteRobot: RemoteRobot) { - doValidTest("PS", remoteRobot) - } - - /** - * Tests to allocate POE datasets with valid parameters. - */ - @Test - fun testAllocateValidPOEDatasets(remoteRobot: RemoteRobot) { - doValidTest("PO-E", remoteRobot) - } - - /** - * Tests to allocate dataset with invalid dataset name, checks that correct message is returned. - */ - @Test - fun testInvalidDatasetName(remoteRobot: RemoteRobot) = with(remoteRobot) { - val message = - "Each name segment (qualifier) is 1 to 8 characters, the first of which must be alphabetic (A to Z) or " + - "national (# @ $). The remaining seven characters are either alphabetic, numeric (0 - 9), national, " + - "a hyphen (-). Name segments are separated by a period (.)" - allocateDataSet( - wsName, "A23456789.A", "PO", "TRK", 10, 1, 1, - "FB", 80, 3200, 0, remoteRobot, false, message - ) - } - - /** - * Tests to allocate dataset with invalid primary allocation, checks that correct message is returned. - */ - @Test - fun testInvalidPrimaryAllocation(remoteRobot: RemoteRobot) { - allocateDataSet( - wsName, "A23.A23", "PO", "TRK", -2, 0, 1, - "FB", 80, 3200, 0, remoteRobot, false, numberGreaterThanOneMsg - ) - } - - /** - * Tests to allocate dataset with invalid directory, checks that correct message is returned. - */ - @Test - fun testInvalidDirectory(remoteRobot: RemoteRobot) { - allocateDataSet( - wsName, "A23.A23", "PO", "TRK", 10, 0, 0, - "FB", 80, 3200, 0, remoteRobot, false, numberGreaterThanOneMsg - ) - } - - /** - * Tests to allocate dataset with invalid record length, checks that correct message is returned. + * Tests to allocate PO, PS, POE datasets with valid parameters. */ - @Test - fun testInvalidRecordLength(remoteRobot: RemoteRobot) { - allocateDataSet( - wsName, "A23.A23", "PO", "TRK", 10, 0, 1, - "FB", 0, 3200, 0, remoteRobot, false, numberGreaterThanOneMsg - ) + @ParameterizedTest + @ValueSource(strings = [SEQUENTIAL_ORG_FULL, PO_ORG_FULL, POE_ORG_FULL]) + fun testAllocateValidPODatasets(input: String, remoteRobot: RemoteRobot) { + doValidTest(input, remoteRobot) } /** - * Tests to allocate dataset with invalid secondary allocation, checks that correct message is returned. + * Tests to allocate dataset with invalid dataset params, checks that correct message is returned. */ - @Test - fun testInvalidSecondaryAllocation(remoteRobot: RemoteRobot) { - allocateDataSet( - wsName, "A23.A23", "PO", "TRK", 10, -10, 1, - "FB", 80, 3200, 0, remoteRobot, false, enterPositiveNumberMsg + @ParameterizedTest + @MethodSource("valuesProvider") + fun testInvalidDatasetName(scenarioName: String, value: InvalidAllocate, remoteRobot: RemoteRobot) = + invalidAllocateDataSet( + wsName, + value.datasetName, + value.datasetOrganization, + value.allocationUnit, + value.primaryAllocation, + value.secondaryAllocation, + value.directory, + value.recordFormat, + value.recordLength, + value.blockSize, + value.averageBlockLength, + remoteRobot, + value.message ) - } - - /** - * Tests to allocate dataset with invalid block size, checks that correct message is returned. - */ - @Test - fun testInvalidBlockSize(remoteRobot: RemoteRobot) { - allocateDataSet( - wsName, "A23.A23", "PO", "TRK", 10, 0, 1, - "FB", 80, -1, 0, remoteRobot, false, enterPositiveNumberMsg - ) - } - - /** - * Tests to allocate dataset with invalid average block length, checks that correct message is returned. - */ - @Test - fun testInvalidAverageBlockLength(remoteRobot: RemoteRobot) { - allocateDataSet( - wsName, "A23.A23", "PO", "TRK", 10, 0, 1, - "FB", 80, 3200, -1, remoteRobot, false, enterPositiveNumberMsg - ) - } /** * Allocates dataset with different record formats. */ private fun doValidTest(datasetOrganization: String, remoteRobot: RemoteRobot) { recordFormats.forEach { s -> - val recordLength = if (s == "F") { + val recordLength = if (s == F_RECORD_FORMAT_SHORT) { 3200 } else { 80 } - val dsName = "${datasetName}${datasetOrganization.replace("-", "")}.${s}".uppercase() - responseDispatcher.injectEndpoint( - "testAllocateValid${datasetOrganization}Datasets_${s}_restfiles", - { it?.requestLine?.contains("POST /zosmf/restfiles/ds/${dsName}") ?: false }, - { MockResponse().setBody("{\"dsorg\":\"${datasetOrganization}\",\"alcunit\":\"TRK\",\"primary\":10,\"secondary\":1,\"dirblk\":1,\"recfm\":\"${s}\",\"blksize\":3200,\"lrecl\":${recordLength}}") } - ) + val dsOrganisationShort = "\\((.*?)\\)".toRegex().find(datasetOrganization)?.groupValues?.get(1) + val dsName = "${datasetMask}${dsOrganisationShort}.${s}".uppercase().replace("-", "") + + if (dsOrganisationShort != null) { + responseDispatcher.injectAllocationResultPo( + datasetOrganization, s, dsName, dsOrganisationShort, recordLength + ) + } allocateDataSet( - wsName, dsName, datasetOrganization, "TRK", 10, 1, 1, - s, recordLength, 3200, 0, remoteRobot, true, "" + wsName, dsName, datasetOrganization, TRACKS_ALLOCATION_UNIT_SHORT, 10, 1, 1, + s, recordLength, 3200, 0, remoteRobot ) - val dsntp = if (datasetOrganization == "PS") { + + val dsntp = if (dsOrganisationShort == SEQUENTIAL_ORG_SHORT) { "" } else { - "PDS" + PDS_TYPE } - val listDs = "{\n" + - " \"dsname\": \"${dsName}\",\n" + - " \"blksz\": \"3200\",\n" + - " \"catnm\": \"TEST.CATALOG.MASTER\",\n" + - " \"cdate\": \"2021/11/15\",\n" + - " \"dev\": \"3390\",\n" + - " \"dsntp\": \"${dsntp}\",\n" + - " \"dsorg\": \"${datasetOrganization}\",\n" + - " \"edate\": \"***None***\",\n" + - " \"extx\": \"1\",\n" + - " \"lrecl\": \"${recordLength}\",\n" + - " \"migr\": \"NO\",\n" + - " \"mvol\": \"N\",\n" + - " \"ovf\": \"NO\",\n" + - " \"rdate\": \"2021/11/17\",\n" + - " \"recfm\": \"${s}\",\n" + - " \"sizex\": \"10\",\n" + - " \"spacu\": \"TRACKS\",\n" + - " \"used\": \"1\",\n" + - " \"vol\": \"TESTVOL\",\n" + - " \"vols\": \"TESTVOL\"\n" + - " }," + val listDs = buildDatasetConfigString(dsName, dsntp, datasetOrganization, recordLength, s) mapListDatasets[dsName] = listDs datasetsToBeDeleted.add(dsName) } @@ -256,7 +166,7 @@ class AllocateDatasetTest { /** * Allocates dataset. */ - private fun allocateDataSet( + private fun invalidAllocateDataSet( wsName: String, datasetName: String, datasetOrganization: String, @@ -269,16 +179,15 @@ class AllocateDatasetTest { blockSize: Int, averageBlockLength: Int, remoteRobot: RemoteRobot, - isValid: Boolean, - message: String + message: String, ) = with(remoteRobot) { - ideFrameImpl(projectName, fixtureStack) { + ideFrameImpl(PROJECT_NAME, fixtureStack) { explorer { fileExplorer.click() find(viewTree).findText(wsName).rightClick() } - actionMenu(remoteRobot, "New").click() - actionMenuItem(remoteRobot, "Dataset").click() + actionMenu(remoteRobot, NEW_POINT_TEXT).click() + actionMenuItem(remoteRobot, DATASET_POINT_TEXT).click() allocateDatasetDialog(fixtureStack) { allocateDataset( datasetName, @@ -292,95 +201,41 @@ class AllocateDatasetTest { blockSize, averageBlockLength ) - clickButton("OK") - Thread.sleep(5000) - } - if (isValid) { - closableFixtureCollector.closeOnceIfExists(AllocateDatasetDialog.name) - find(byXpath("//div[@class='MyDialog']")).findText("Dataset $datasetName Has Been Created") - clickButton("No") - } else { - val msgAll = find( - byXpath("//div[@class='HeavyWeightWindow']"), - Duration.ofSeconds(30) - ).findAllText() - var msg = "" - msgAll.forEach { msg += it.text } - msg.shouldContain(message) - assertFalse(button("OK").isEnabled()) - clickButton("Cancel") - closableFixtureCollector.closeOnceIfExists(AllocateDatasetDialog.name) + clickButton(OK_TEXT) } - } - } - /** - * Deletes created datasets. - */ - private fun deleteDatasets(remoteRobot: RemoteRobot) = with(remoteRobot) { - responseDispatcher.injectEndpoint( - "listAllAllocatedDatasets_restfiles", - { it?.requestLine?.contains("GET /zosmf/restfiles/ds?dslevel=${datasetName.uppercase()}*") ?: false }, - { MockResponse().setBody(buildFinalListDatasetJson()) } - ) - responseDispatcher.injectEndpoint( - "listMembers_restfiles", - { it?.requestLine?.contains("/member") ?: false }, - { MockResponse().setBody("{\"items\":[],\"returnedRows\":0,\"totalRows\":0,\"moreRows\":null,\"JSONversion\":1}") } - ) - ideFrameImpl(projectName, fixtureStack) { - explorer { - fileExplorer.click() - find(viewTree).findAllText(wsName).last().doubleClick() - Thread.sleep(10000) - } - } - datasetsToBeDeleted.forEach { s -> - mapListDatasets.remove(s) - responseDispatcher.injectEndpoint( - "listAllocatedDatasets_restfiles_${s}", - { it?.requestLine?.contains("GET /zosmf/restfiles/ds?dslevel=${datasetName.uppercase()}*") ?: false }, - { MockResponse().setBody(buildFinalListDatasetJson()) } - ) - responseDispatcher.injectEndpoint( - "deleteDataset_restfiles_${s}", - { it?.requestLine?.contains("DELETE /zosmf/restfiles/ds/${s}") ?: false }, - { MockResponse().setBody("{}") } - ) - deleteDataset(s, projectName, fixtureStack, remoteRobot) - } - } + val msgAll = find( + messageLoc, + Duration.ofSeconds(30) + ).findAllText() + var msg = "" + msgAll.forEach { msg += it.text } + msg.shouldContain(message) + assertFalse(button(OK_TEXT).isEnabled()) + clickButton(CANCEL_TEXT) + closableFixtureCollector.closeOnceIfExists(AllocateDatasetDialog.name) - private fun buildFinalListDatasetJson(): String { - var result = "{}" - if (mapListDatasets.isNotEmpty()) { - var listDatasetsJson = "{\"items\":[" - mapListDatasets.forEach { - listDatasetsJson += it.value - } - result = listDatasetsJson.dropLast(1) + "],\n" + - " \"returnedRows\": ${mapListDatasets.size},\n" + - " \"totalRows\": ${mapListDatasets.size},\n" + - " \"JSONversion\": 1\n" + - "}" } - return result } +// private val datasetMask = "$ZOS_USERID.ALLOC." /** - * Creates working set and z/OS mask. + * Deletes created datasets. */ - private fun createWsAndMask(remoteRobot: RemoteRobot) = with(remoteRobot) { - createWsWithoutMask(projectName, wsName, connectionName, fixtureStack, closableFixtureCollector, remoteRobot) - ideFrameImpl(projectName, fixtureStack) { - createMask(wsName, fixtureStack, closableFixtureCollector) - createMaskDialog(fixtureStack) { - createMask(Pair("$datasetName*", "z/OS")) - Thread.sleep(3000) - clickButton("OK") - } - closableFixtureCollector.closeOnceIfExists(CreateMaskDialog.name) + private fun deleteDatasets(remoteRobot: RemoteRobot) { + injectListAllAllocatedDatasets(datasetName.uppercase(), mapListDatasets) + responseDispatcher.injectListMembers(NO_MEMBERS) + refreshWorkSpace(wsName, fixtureStack, remoteRobot) + compressAndDecompressTree(wsName, fixtureStack, remoteRobot) + + datasetsToBeDeleted.forEach { s -> + mapListDatasets.remove(s) + responseDispatcher.injectAllocatedDatasets(s, buildFinalListDatasetJson(mapListDatasets), s) + responseDispatcher.injectDeleteDataset(s) + deleteDataset(s, fixtureStack, remoteRobot) } + compressAndDecompressTree(wsName, fixtureStack, remoteRobot) + return datasetsToBeDeleted.clear() } } \ No newline at end of file diff --git a/src/uiTest/kotlin/workingset/CreateUssFileAndDirTest.kt b/src/uiTest/kotlin/workingset/CreateUssFileAndDirTest.kt index 1b435782c..d7d0a153f 100644 --- a/src/uiTest/kotlin/workingset/CreateUssFileAndDirTest.kt +++ b/src/uiTest/kotlin/workingset/CreateUssFileAndDirTest.kt @@ -73,9 +73,8 @@ class CreateUssFileAndDirTest { { it?.requestLine?.contains("zosmf/resttopology/systems") ?: false }, { MockResponse().setBody(responseDispatcher.readMockJson("infoResponse") ?: "") } ) - setUpTestEnvironment(projectName, fixtureStack, closableFixtureCollector, remoteRobot) + setUpTestEnvironment(fixtureStack, closableFixtureCollector, remoteRobot) createConnection( - projectName, fixtureStack, closableFixtureCollector, connectionName, @@ -83,7 +82,7 @@ class CreateUssFileAndDirTest { remoteRobot, "https://${mockServer.hostName}:${mockServer.port}" ) - createWsWithoutMask(projectName, wsName, connectionName, fixtureStack, closableFixtureCollector, remoteRobot) + createWsWithoutMask(wsName, connectionName, fixtureStack, closableFixtureCollector, remoteRobot) mapListUssFiles["."] = dirHereList mapListUssFiles[".."] = dirParentList responseDispatcher.injectEndpoint( @@ -91,7 +90,7 @@ class CreateUssFileAndDirTest { { it?.requestLine?.contains("GET /zosmf/restfiles/fs?path") ?: false }, { MockResponse().setBody(buildResponseListJson(mapListUssFiles, true)) } ) - ideFrameImpl(projectName, fixtureStack) { + ideFrameImpl(PROJECT_NAME, fixtureStack) { createMask(wsName, fixtureStack, closableFixtureCollector) createMaskDialog(fixtureStack) { createMask(Pair(ussMaskName, "USS")) @@ -113,8 +112,8 @@ class CreateUssFileAndDirTest { fun tearDownAll(remoteRobot: RemoteRobot) = with(remoteRobot) { mockServer.shutdown() - clearEnvironment(projectName, fixtureStack, closableFixtureCollector, remoteRobot) - ideFrameImpl(projectName, fixtureStack) { + clearEnvironment(fixtureStack, closableFixtureCollector, remoteRobot) + ideFrameImpl(PROJECT_NAME, fixtureStack) { close() } } @@ -140,8 +139,8 @@ class CreateUssFileAndDirTest { { it?.requestLine?.contains("GET /zosmf/restfiles/fs?path") ?: false }, { MockResponse().setBody(buildResponseListJson(mapListUssFiles, true)) } ) - ideFrameImpl(projectName, fixtureStack) { - createUssFile(ussMaskName, ussFileName, UssFileType.File, projectName, fixtureStack, remoteRobot) + ideFrameImpl(PROJECT_NAME, fixtureStack) { + createUssFile(ussMaskName, ussFileName, UssFileType.File, fixtureStack, remoteRobot) mapListUssFiles[ussFileName] = fileList explorer { fileExplorer.click() @@ -162,7 +161,7 @@ class CreateUssFileAndDirTest { { it?.requestLine?.contains("GET /zosmf/restfiles/fs?path") ?: false }, { MockResponse().setBody(buildResponseListJson(mapListUssFiles, true)) } ) - ideFrameImpl(projectName, fixtureStack) { + ideFrameImpl(PROJECT_NAME, fixtureStack) { explorer { fileExplorer.click() find(viewTree).findText(ussMaskName).rightClick() @@ -192,7 +191,7 @@ class CreateUssFileAndDirTest { { it?.requestLine?.contains("GET /zosmf/restfiles/fs?path") ?: false }, { MockResponse().setBody(buildResponseListJson(mapListUssFiles, true)) } ) - ideFrameImpl(projectName, fixtureStack) { + ideFrameImpl(PROJECT_NAME, fixtureStack) { explorer { fileExplorer.click() find(viewTree).findText(ussMaskName).rightClick() @@ -230,7 +229,7 @@ class CreateUssFileAndDirTest { .setBody("{\"category\":\"1.0\",\"message\":\"The specified file already exists\",\"rc\":\"4.0\",\"reason\":\"19.0\"}") } ) - ideFrameImpl(projectName, fixtureStack) { + ideFrameImpl(PROJECT_NAME, fixtureStack) { explorer { fileExplorer.click() find(viewTree).findText(ussMaskName).rightClick() @@ -242,7 +241,8 @@ class CreateUssFileAndDirTest { clickButton("OK") } clickButton("Cancel") - checkErrorNotification(errorHeader, errorType, fileErrorDetail, projectName, fixtureStack, remoteRobot) + checkErrorNotification(fileErrorDetail, errorType, fileErrorDetail, fixtureStack, remoteRobot) + closeNotificztion(fixtureStack, remoteRobot) } } @@ -262,8 +262,8 @@ class CreateUssFileAndDirTest { { it?.requestLine?.contains("POST /zosmf/restfiles/fs$ussMaskName/$ussDirName") ?: false }, { MockResponse().setResponseCode(201) } ) - ideFrameImpl(projectName, fixtureStack) { - createUssFile(ussMaskName, ussDirName, UssFileType.Directory, projectName, fixtureStack, remoteRobot) + ideFrameImpl(PROJECT_NAME, fixtureStack) { + createUssFile(ussMaskName, ussDirName, UssFileType.Directory, fixtureStack, remoteRobot) mapListUssFiles[ussDirName] = dirList explorer { fileExplorer.click() @@ -292,7 +292,7 @@ class CreateUssFileAndDirTest { .setBody("{\"category\":\"1.0\",\"message\":\"The specified file already exists\",\"rc\":\"4.0\",\"reason\":\"19.0\"}") } ) - ideFrameImpl(projectName, fixtureStack) { + ideFrameImpl(PROJECT_NAME, fixtureStack) { explorer { fileExplorer.click() find(viewTree).findText(ussMaskName).rightClick() @@ -304,7 +304,8 @@ class CreateUssFileAndDirTest { clickButton("OK") } clickButton("Cancel") - checkErrorNotification(errorHeader, errorType, fileErrorDetail, projectName, fixtureStack, remoteRobot) + checkErrorNotification(fileErrorDetail, errorType, fileErrorDetail, fixtureStack, remoteRobot) + closeNotificztion(fixtureStack, remoteRobot) } } @@ -319,7 +320,7 @@ class CreateUssFileAndDirTest { { it?.requestLine?.contains("GET /zosmf/restfiles/fs?path") ?: false }, { MockResponse().setBody(buildResponseListJson(mapListUssFiles, true)) } ) - ideFrameImpl(projectName, fixtureStack) { + ideFrameImpl(PROJECT_NAME, fixtureStack) { explorer { fileExplorer.click() find(viewTree).findText(ussMaskName).rightClick() @@ -349,7 +350,7 @@ class CreateUssFileAndDirTest { { it?.requestLine?.contains("GET /zosmf/restfiles/fs?path") ?: false }, { MockResponse().setBody(buildResponseListJson(mapListUssFiles, true)) } ) - ideFrameImpl(projectName, fixtureStack) { + ideFrameImpl(PROJECT_NAME, fixtureStack) { explorer { fileExplorer.click() find(viewTree).findText(ussMaskName).rightClick() @@ -387,7 +388,7 @@ class CreateUssFileAndDirTest { .setBody("{\"category\":\"1.0\",\"message\":\"The specified directory already exists\",\"rc\":\"4.0\",\"reason\":\"19.0\"}") } ) - ideFrameImpl(projectName, fixtureStack) { + ideFrameImpl(PROJECT_NAME, fixtureStack) { explorer { fileExplorer.click() find(viewTree).findText(ussMaskName).rightClick() @@ -399,7 +400,8 @@ class CreateUssFileAndDirTest { clickButton("OK") } clickButton("Cancel") - checkErrorNotification(errorHeader, errorType, dirErrorDetail, projectName, fixtureStack, remoteRobot) + checkErrorNotification(dirErrorDetail, errorType, dirErrorDetail, fixtureStack, remoteRobot) + closeNotificztion(fixtureStack, remoteRobot) } } @@ -422,7 +424,7 @@ class CreateUssFileAndDirTest { .setBody("{\"category\":\"1.0\",\"message\":\"The specified directory already exists\",\"rc\":\"4.0\",\"reason\":\"19.0\"}") } ) - ideFrameImpl(projectName, fixtureStack) { + ideFrameImpl(PROJECT_NAME, fixtureStack) { explorer { fileExplorer.click() find(viewTree).findText(ussMaskName).rightClick() @@ -434,7 +436,8 @@ class CreateUssFileAndDirTest { clickButton("OK") } clickButton("Cancel") - checkErrorNotification(errorHeader, errorType, dirErrorDetail, projectName, fixtureStack, remoteRobot) + checkErrorNotification(dirErrorDetail, errorType, dirErrorDetail, fixtureStack, remoteRobot) + closeNotificztion(fixtureStack, remoteRobot) } } } \ No newline at end of file diff --git a/src/uiTest/kotlin/workingset/Data.kt b/src/uiTest/kotlin/workingset/Data.kt deleted file mode 100644 index ef6a17441..000000000 --- a/src/uiTest/kotlin/workingset/Data.kt +++ /dev/null @@ -1,102 +0,0 @@ -package workingset - -//BUTTON TEXT -const val NO_TEXT = "No" -const val OK_TEXT = "OK" -const val CANCEL_TEXT = "Cancel" - -// Datasets types -const val PARTITION_NAME_FULL = "Partitioned (PO)" -const val PARTITION_NAME_SHORT = "PO" -const val SEQUENTIAL_NAME_FULL = "Sequential (PS)" -const val SEQUENTIAL_NAME_SHORT = "PS" -const val PARTITIONED_EXTENDED_NAME_FULL = "Partitioned Extended (PO-E)" -const val PARTITIONED_EXTENDED_NAME_SHORT = "POE" - -//rename dataset -const val DATASET_FOR_RENAME_PROPERTY = "{\"dsorg\":\"PO\",\"alcunit\":\"TRK\",\"primary\":10,\"secondary\":1,\"dirblk\":2,\"recfm\":\"VB\",\"blksize\":6120,\"lrecl\":255}" - -//record formats -const val F_RECORD_FORMAT_SHORT = "F" -const val FB_RECORD_FORMAT_SHORT = "FB" -const val V_RECORD_FORMAT_SHORT = "V" -const val VA_RECORD_FORMAT_SHORT = "VA" -const val VB_RECORD_FORMAT_SHORT = "VB" - -//action menu points -const val REFRESH_POINT_TEXT = "Refresh" -const val NEW_POINT_TEXT = "New" -const val DATASET_POINT_TEXT = "Dataset" -const val MIGRATE_POINT_TEXT="Migrate" - -//Errors messages -val invalidDatasetNameConstant = "Each name segment (qualifier) is 1 to 8 characters, the first of which must be alphabetic (A to Z) or " + - "national (# @ $). The remaining seven characters are either alphabetic, numeric (0 - 9), national, " + - "a hyphen (-). Name segments are separated by a period (.)" -val numberGreaterThanOneMsg = "Enter a number greater than or equal to 1" -val enterPositiveNumberMsg = "Enter a positive number" - -//Migrate options: -const val HMIGRATE_MIGRATE_OPTIONS = "hmigrate" -const val HRECALL_MIGRATE_OPTIONS = "hrecall" - - -//dialog text\patterns - -val datasetHasBeenCreated = "Dataset %s Has Been Created " - - -//bad alloc params cases -data class InvalidAllocate( - val wsName: String, - val datasetName: String, - val datasetOrganization: String, - val allocationUnit: String, - val primaryAllocation: Int, - val secondaryAllocation: Int, - val directory: Int, - val recordFormat: String, - val recordLength: Int, - val blockSize: Int, - val averageBlockLength: Int, - val message: String -) - -val invalidDatasetNameParams = InvalidAllocate( - "", "A23456789.A", PARTITION_NAME_FULL, "TRK", 10, 1, - 1, "FB", 80, 3200, 0, invalidDatasetNameConstant -) -val invalidPrimaryAllocationParams = InvalidAllocate( - "", "A23.A23", PARTITION_NAME_FULL, "TRK", -2, 0, 1, - "FB", 80, 3200, 0, numberGreaterThanOneMsg) - -val invalidDirectoryParams = InvalidAllocate( - "", "A23.A23", PARTITION_NAME_FULL, "TRK", 10, 0, 0, - "FB", 80, 3200, 0,numberGreaterThanOneMsg -) -val invalidRecordLengthParams = InvalidAllocate( - "", "A23.A23", PARTITION_NAME_FULL, "TRK", 10, 0, 1, - "FB", 0, 3200, 0,numberGreaterThanOneMsg -) -val invalidSecondaryAllocationParams = InvalidAllocate( - "", "A23.A23", PARTITION_NAME_FULL, "TRK", 10, -10, 1, - "FB", 80, 3200, 0, enterPositiveNumberMsg -) -val invalidBlockSizeParams = InvalidAllocate( - "", "A23.A23", PARTITION_NAME_FULL, "TRK", 10, 0, 1, - "FB", 80, -1, 0, enterPositiveNumberMsg -) -val invalidAverageBlockLengthParams = InvalidAllocate( - "", "A23.A23", PARTITION_NAME_FULL, "TRK", 10, 0, 1, - "FB", 80, 3200, -1, enterPositiveNumberMsg -) - -val invalidAllocateScenarios = mapOf( - Pair("invalidDatasetNameParams", invalidDatasetNameParams), - Pair("invalidPrimaryAllocationParams", invalidPrimaryAllocationParams), - Pair("invalidDirectoryParams", invalidDirectoryParams), - Pair("invalidRecordLengthParams", invalidRecordLengthParams), - Pair("invalidSecondaryAllocationParams", invalidSecondaryAllocationParams), - Pair("invalidBlockSizeParams", invalidBlockSizeParams), - Pair("invalidAverageBlockLengthParams", invalidAverageBlockLengthParams), -) diff --git a/src/uiTest/kotlin/workingset/DeleteDatasetTest.kt b/src/uiTest/kotlin/workingset/DeleteDatasetTest.kt index e3a7efa72..c9af91075 100644 --- a/src/uiTest/kotlin/workingset/DeleteDatasetTest.kt +++ b/src/uiTest/kotlin/workingset/DeleteDatasetTest.kt @@ -10,73 +10,58 @@ package workingset + import auxiliary.* import auxiliary.closable.ClosableFixtureCollector -import auxiliary.components.actionMenu -import auxiliary.components.actionMenuItem -import auxiliary.containers.* import com.intellij.remoterobot.RemoteRobot -import com.intellij.remoterobot.fixtures.ComponentFixture -import com.intellij.remoterobot.fixtures.ContainerFixture import com.intellij.remoterobot.search.locators.Locator -import com.intellij.remoterobot.search.locators.byXpath -import okhttp3.mockwebserver.MockResponse import org.junit.jupiter.api.* import org.junit.jupiter.api.extension.ExtendWith import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.provider.ValueSource +import workingset.testutils.injectListAllAllocatedDatasets + /** * Tests allocating datasets with valid and invalid inputs. */ @TestInstance(TestInstance.Lifecycle.PER_CLASS) @ExtendWith(RemoteRobotExtension::class) -class DeleteDatasetTest:WorkingSetBase() { +class DeleteDatasetTest : WorkingSetBase() { private var closableFixtureCollector = ClosableFixtureCollector() private var fixtureStack = mutableListOf() - private var wantToClose = mutableListOf( - "Allocate Dataset Dialog" - ) - private val projectName = "untitled" - override val connectionName = "valid connection" - - val wsName = "WS1" + private var wantToClose = mutableListOf(ALLOCATE_DATASET_DIALOG) override val datasetName = "$ZOS_USERID.ALLOC." - private val recordFormats = mutableListOf("F", "FB", "V", "VA", "VB") + private val recordFormats = mutableListOf(F_RECORD_FORMAT_SHORT, FB_RECORD_FORMAT_SHORT, V_RECORD_FORMAT_SHORT, VA_RECORD_FORMAT_SHORT, VB_RECORD_FORMAT_SHORT) private var datasetsToBeDeleted = mutableListOf() - - private var mapListDatasets = mutableMapOf() + private var mapListDatasets = mutableMapOf() /** * Opens the project and Explorer, clears test environment, creates working set and mask. */ @BeforeAll - fun setUpAll(testInfo: TestInfo, remoteRobot: RemoteRobot) = with(remoteRobot) { + fun setUpAll(testInfo: TestInfo, remoteRobot: RemoteRobot) { startMockServer() - setUpTestEnvironment(projectName, fixtureStack, closableFixtureCollector, remoteRobot) + setUpTestEnvironment(fixtureStack, closableFixtureCollector, remoteRobot) createValidConnectionWithMock( testInfo, connectionName, - projectName, fixtureStack, closableFixtureCollector, remoteRobot ) createWsAndMask(remoteRobot) - } /** * Closes the project and clears test environment, deletes created datasets. */ @AfterAll - fun tearDownAll(remoteRobot: RemoteRobot) = with(remoteRobot) { + fun tearDownAll(remoteRobot: RemoteRobot) { deleteDatasets(remoteRobot) mockServer.shutdown() - clearEnvironment(projectName, fixtureStack, closableFixtureCollector, remoteRobot) - ideFrameImpl(projectName, fixtureStack) { - close() - } + clearEnvironment(fixtureStack, closableFixtureCollector, remoteRobot) + closeIntelligentProject(fixtureStack, remoteRobot) } /** @@ -94,7 +79,7 @@ class DeleteDatasetTest:WorkingSetBase() { * Tests to allocate PO datasets with valid parameters. */ @ParameterizedTest - @ValueSource(strings = ["Sequential (PS)", "Partitioned (PO)", "Partitioned Extended (PO-E)"]) + @ValueSource(strings = [SEQUENTIAL_ORG_FULL, PO_ORG_FULL, POE_ORG_FULL]) fun testDeleteDatasets(input: String, remoteRobot: RemoteRobot) { doValidTest(input, remoteRobot) deleteDatasets(remoteRobot) @@ -105,53 +90,35 @@ class DeleteDatasetTest:WorkingSetBase() { */ private fun doValidTest(datasetOrganization: String, remoteRobot: RemoteRobot) { recordFormats.forEach { s -> - val recordLength = if (s == "F") { + val recordLength = if (s == F_RECORD_FORMAT_SHORT) { 3200 } else { 80 } -// val dsName = "${datasetName}${datasetOrganization.replace("-", "")}.${s}".uppercase() val dsOrganisationShort = "\\((.*?)\\)".toRegex().find(datasetOrganization)?.groupValues?.get(1) val dsName = "${datasetName}${dsOrganisationShort}.${s}".uppercase().replace("-", "") - responseDispatcher.injectEndpoint( - "testAllocateValid${datasetOrganization}Datasets_${s}_restfiles", - { it?.requestLine?.contains("POST /zosmf/restfiles/ds/${dsName}") ?: false }, - { MockResponse().setBody("{\"dsorg\":\"${dsOrganisationShort}\",\"alcunit\":\"TRK\",\"primary\":10,\"secondary\":1,\"dirblk\":1,\"recfm\":\"${s}\",\"blksize\":3200,\"lrecl\":${recordLength}}") } - ) -// Thread.sleep(3000) + + if (dsOrganisationShort != null) { + responseDispatcher.injectAllocationResultPo( + datasetOrganization, + s, + dsName, + dsOrganisationShort, + recordLength + ) + } allocateDataSet( - wsName, dsName, datasetOrganization, "TRK", 10, 1, 1, + wsName, dsName, datasetOrganization, TRACKS_ALLOCATION_UNIT_SHORT, 10, 1, 1, s, recordLength, 3200, 0, remoteRobot ) - val dsntp = if (dsOrganisationShort == "PS") { + val dsntp = if (dsOrganisationShort == SEQUENTIAL_ORG_SHORT) { "" } else { - "PDS" + PDS_TYPE } - val listDs = "{\n" + - " \"dsname\": \"${dsName}\",\n" + - " \"blksz\": \"3200\",\n" + - " \"catnm\": \"TEST.CATALOG.MASTER\",\n" + - " \"cdate\": \"2021/11/15\",\n" + - " \"dev\": \"3390\",\n" + - " \"dsntp\": \"${dsntp}\",\n" + - " \"dsorg\": \"${datasetOrganization}\",\n" + - " \"edate\": \"***None***\",\n" + - " \"extx\": \"1\",\n" + - " \"lrecl\": \"${recordLength}\",\n" + - " \"migr\": \"NO\",\n" + - " \"mvol\": \"N\",\n" + - " \"ovf\": \"NO\",\n" + - " \"rdate\": \"2021/11/17\",\n" + - " \"recfm\": \"${s}\",\n" + - " \"sizex\": \"10\",\n" + - " \"spacu\": \"TRACKS\",\n" + - " \"used\": \"1\",\n" + - " \"vol\": \"TESTVOL\",\n" + - " \"vols\": \"TESTVOL\"\n" + - " }," + val listDs = buildDatasetConfigString(dsName, dsntp, datasetOrganization, recordLength, s) mapListDatasets[dsName] = listDs datasetsToBeDeleted.add(dsName) } @@ -160,68 +127,21 @@ class DeleteDatasetTest:WorkingSetBase() { /** * Deletes created datasets. */ - private fun deleteDatasets(remoteRobot: RemoteRobot) = with(remoteRobot) { - responseDispatcher.injectEndpoint( - "listAllAllocatedDatasets_restfiles", - { it?.requestLine?.contains("GET /zosmf/restfiles/ds?dslevel=${datasetName.uppercase()}*") ?: false }, - { MockResponse().setBody(buildFinalListDatasetJson()) } - ) - responseDispatcher.injectEndpoint( - "listMembers_restfiles", - { it?.requestLine?.contains("/member") ?: false }, - { MockResponse().setBody("{\"items\":[],\"returnedRows\":0,\"totalRows\":0,\"moreRows\":null,\"JSONversion\":1}") } - ) - ideFrameImpl(projectName, fixtureStack) { - explorer { - fileExplorer.click() - find(viewTree).findText(wsName).rightClick() - } - actionMenuItem(remoteRobot, "Refresh").click() - explorer { - find(viewTree).findAllText(wsName).last().doubleClick() - } - } -// Thread.sleep(1000) -// Thread.sleep(3000) + private fun deleteDatasets(remoteRobot: RemoteRobot) { + injectListAllAllocatedDatasets(datasetName.uppercase(), mapListDatasets) + responseDispatcher.injectListMembers(NO_MEMBERS) + + refreshWorkSpace(wsName, fixtureStack, remoteRobot) + compressAndDecompressTree(wsName, fixtureStack, remoteRobot) + datasetsToBeDeleted.forEach { s -> mapListDatasets.remove(s) - responseDispatcher.injectEndpoint( - "listAllocatedDatasets_restfiles_${s}", - { it?.requestLine?.contains("GET /zosmf/restfiles/ds?dslevel=${datasetName.uppercase()}*") ?: false }, - { MockResponse().setBody(buildFinalListDatasetJson()) } - ) - responseDispatcher.injectEndpoint( - "deleteDataset_restfiles_${s}", - { it?.requestLine?.contains("DELETE /zosmf/restfiles/ds/${s}") ?: false }, - { MockResponse().setBody("{}") } - ) - deleteDataset(s, projectName, fixtureStack, remoteRobot) - - } - ideFrameImpl(projectName, fixtureStack) { - explorer { - fileExplorer.click() - find(viewTree).findAllText(wsName).last().doubleClick() -// Thread.sleep(1000) - } + responseDispatcher.injectAllocatedDatasets(s, buildFinalListDatasetJson(mapListDatasets), s) + responseDispatcher.injectDeleteDataset(s) + deleteDataset(s, fixtureStack, remoteRobot) } + compressAndDecompressTree(wsName, fixtureStack, remoteRobot) + datasetsToBeDeleted.clear() } - - - -// /** -// * Creates working set and z/OS mask. -// */ -// private fun createWsAndMask(remoteRobot: RemoteRobot) = with(remoteRobot) { -// createWsWithoutMask(projectName, wsName, connectionName, fixtureStack, closableFixtureCollector, remoteRobot) -// ideFrameImpl(projectName, fixtureStack) { -// createMask(wsName, fixtureStack, closableFixtureCollector) -// createMaskDialog(fixtureStack) { -// createMask(Pair("$datasetName*", "z/OS")) -// clickButton("OK") -// } -// closableFixtureCollector.closeOnceIfExists(CreateMaskDialog.name) -// } -// } } \ No newline at end of file diff --git a/src/uiTest/kotlin/workingset/Locators.kt b/src/uiTest/kotlin/workingset/Locators.kt deleted file mode 100644 index 1b3639ec9..000000000 --- a/src/uiTest/kotlin/workingset/Locators.kt +++ /dev/null @@ -1,13 +0,0 @@ -package workingset - -import com.intellij.remoterobot.search.locators.byXpath - -//common dialogs locators -val myDialogXpathLoc = byXpath("//div[@class='MyDialog']") - - -//allocate dialog locators -val datasetNameInputLoc = byXpath("//div[@class='JBTextField']") -val datasetOrgDropDownLoc = byXpath("//div[@class='ComboBox']") -val inputFieldLoc = byXpath("//div[@class='JBTextField']") -val dropdownsLoc = byXpath("//div[@class='ComboBox']") \ No newline at end of file diff --git a/src/uiTest/kotlin/workingset/MigrateDatasetTest.kt b/src/uiTest/kotlin/workingset/MigrateDatasetTest.kt index 2110af430..a5532fa60 100644 --- a/src/uiTest/kotlin/workingset/MigrateDatasetTest.kt +++ b/src/uiTest/kotlin/workingset/MigrateDatasetTest.kt @@ -12,21 +12,12 @@ package workingset import auxiliary.* import auxiliary.closable.ClosableFixtureCollector -import auxiliary.components.actionMenu -import auxiliary.components.actionMenuItem import auxiliary.containers.* import com.intellij.remoterobot.RemoteRobot -import com.intellij.remoterobot.fixtures.ComponentFixture -import com.intellij.remoterobot.fixtures.ContainerFixture -import com.intellij.remoterobot.fixtures.HeavyWeightWindowFixture import com.intellij.remoterobot.search.locators.Locator -import com.intellij.remoterobot.search.locators.byXpath -import io.kotest.matchers.string.shouldContain -import okhttp3.mockwebserver.MockResponse import org.junit.jupiter.api.* -import org.junit.jupiter.api.Assertions.assertFalse import org.junit.jupiter.api.extension.ExtendWith -import java.time.Duration +import workingset.testutils.injectListAllAllocatedDatasets /** * Tests migrating and recalling dataset. @@ -34,13 +25,13 @@ import java.time.Duration @TestMethodOrder(MethodOrderer.OrderAnnotation::class) @TestInstance(TestInstance.Lifecycle.PER_CLASS) @ExtendWith(RemoteRobotExtension::class) -class MigrateDatasetTest { +class MigrateDatasetTest:WorkingSetBase() { private var closableFixtureCollector = ClosableFixtureCollector() private var fixtureStack = mutableListOf() private val projectName = "untitled" - private val connectionName = "con1" - private val wsName = "WS name" + override val connectionName = "con1" + override val wsName = "WS name" private val maskName = "${ZOS_USERID.uppercase()}.UI.TEST*" private val pdsName = "${ZOS_USERID.uppercase()}.UI.TEST" @@ -55,112 +46,43 @@ class MigrateDatasetTest { @BeforeAll fun setUpAll(testInfo: TestInfo,remoteRobot: RemoteRobot) = with(remoteRobot) { startMockServer() - responseDispatcher.injectEndpoint( - "${testInfo.displayName}_info", - { it?.requestLine?.contains("zosmf/info") ?: false }, - { MockResponse().setBody(responseDispatcher.readMockJson("infoResponse") ?: "") } - ) - responseDispatcher.injectEndpoint( - "${testInfo.displayName}_resttopology", - { it?.requestLine?.contains("zosmf/resttopology/systems") ?: false }, - { MockResponse().setBody(responseDispatcher.readMockJson("infoResponse") ?: "") } - ) - setUpTestEnvironment(projectName, fixtureStack, closableFixtureCollector, remoteRobot) - createConnection( - projectName, - fixtureStack, - closableFixtureCollector, - connectionName, - true, - remoteRobot, - "https://${mockServer.hostName}:${mockServer.port}" - ) - responseDispatcher.injectEndpoint( - "listAllAllocatedDatasets_restfiles", - { it?.requestLine?.contains("GET /zosmf/restfiles/ds?dslevel=${maskName}") ?: false }, - { MockResponse().setBody(buildResponseListJson(mapListDatasets, true)) } - ) - responseDispatcher.injectEndpoint( - "listAllDatasetMembers_restfiles", - { it?.requestLine?.contains("GET /zosmf/restfiles/ds/$pdsName/member") ?: false }, - { MockResponse().setBody(buildResponseListJson(mapListDatasetMembers, false)) } - ) - responseDispatcher.injectEndpoint( - "allocatePds_restfiles", - { it?.requestLine?.contains("POST /zosmf/restfiles/ds/$pdsName") ?: false }, - { MockResponse().setBody("{\"dsorg\":\"PDS\",\"alcunit\":\"TRK\",\"primary\":10,\"secondary\":1,\"dirblk\":2,\"recfm\":\"VB\",\"blksize\":6120,\"lrecl\":255}") } - ) - createWsWithoutMask(projectName, wsName, connectionName, fixtureStack, closableFixtureCollector, remoteRobot) - mapListDatasets[pdsName] = listDS(pdsName, "PDS", "PO") - allocatePDSAndCreateMask( - wsName, - pdsName, - projectName, - fixtureStack, - closableFixtureCollector, - remoteRobot, - maskName, - directory = 2 + responseDispatcher.injectTestInfo(testInfo) + responseDispatcher.injectTestInfoRestTopology(testInfo) + setUpTestEnvironment(fixtureStack, closableFixtureCollector, remoteRobot) + createConnection(fixtureStack, closableFixtureCollector, connectionName, true, remoteRobot, "https://${mockServer.hostName}:${mockServer.port}") + mapListDatasets[pdsName] = auxiliary.listDS(pdsName, PDS_TYPE, PO_ORG_SHORT) + injectListAllAllocatedDatasets(pdsName, mapListDatasets, true) + responseDispatcher.injectListAllDatasetMembersRestfiles(pdsName, buildResponseListJson(mapListDatasetMembers, false)) + responseDispatcher.injectAllocationResultPds(pdsName) + + createWsWithoutMask(wsName, connectionName, fixtureStack, closableFixtureCollector, remoteRobot) + + allocatePDSAndCreateMask(wsName, pdsName,fixtureStack, closableFixtureCollector, remoteRobot, maskName, directory = 2 ) } + /** * Closes the project and clears test environment. */ @AfterAll fun tearDownAll(remoteRobot: RemoteRobot) = with(remoteRobot) { mockServer.shutdown() - clearEnvironment(projectName, fixtureStack, closableFixtureCollector, remoteRobot) - ideFrameImpl(projectName, fixtureStack) { + clearEnvironment(fixtureStack, closableFixtureCollector, remoteRobot) + ideFrameImpl(PROJECT_NAME, fixtureStack) { close() } + responseDispatcher.removeAllEndpoints() } /** * Test to migrate and recall dataset */ @Test - @Order(1) fun testViewJobProperties(remoteRobot: RemoteRobot) = with(remoteRobot) { - ideFrameImpl(projectName, fixtureStack) { - explorer { - fileExplorer.click() - Thread.sleep(1000) - find(viewTree).findText(pdsName).rightClick() - } - responseDispatcher.injectEndpoint( - "migratePds_restfiles", - { it?.requestLine?.contains("PUT /zosmf/restfiles/ds/$pdsName") ?: false && - it?.body?.toString()?.contains("hmigrate") ?: false }, - { MockResponse().setResponseCode(200) } - ) - mapListDatasets[pdsName] = migratedDs - actionMenuItem(remoteRobot, "Migrate").click() - explorer { - fileExplorer.click() - Thread.sleep(1000) - find(viewTree).findText(pdsName).rightClick() - } - actionMenuItem(remoteRobot, "Properties").click() - datasetPropertiesDialog(fixtureStack) { - if (isDatasetMigrated()) { - throw Exception("Dataset is not migrated") - } - clickButton("OK") - } - responseDispatcher.injectEndpoint( - "migratePds_restfiles", - { it?.requestLine?.contains("PUT /zosmf/restfiles/ds/$pdsName") ?: false && - it?.body?.toString()?.contains("hrecall") ?: false }, - { MockResponse().setResponseCode(200) } - ) - explorer { - fileExplorer.click() - Thread.sleep(1000) - find(viewTree).findText(pdsName).rightClick() - } - mapListDatasets[pdsName] = listDS(pdsName, "PDS", "PO") - actionMenuItem(remoteRobot, "Recall").click() + ideFrameImpl(PROJECT_NAME, fixtureStack) { + migrateDataset(pdsName, migratedDs, mapListDatasets, fixtureStack, remoteRobot) + recallDataset(pdsName, fixtureStack, mapListDatasets, remoteRobot) } } } \ No newline at end of file diff --git a/src/uiTest/kotlin/workingset/RenameDatasetTest.kt b/src/uiTest/kotlin/workingset/RenameDatasetTest.kt index aab37ede2..b4fb54d1c 100644 --- a/src/uiTest/kotlin/workingset/RenameDatasetTest.kt +++ b/src/uiTest/kotlin/workingset/RenameDatasetTest.kt @@ -12,15 +12,20 @@ package workingset import auxiliary.* import auxiliary.closable.ClosableFixtureCollector -import auxiliary.components.actionMenuItem -import auxiliary.containers.* import com.intellij.remoterobot.RemoteRobot -import com.intellij.remoterobot.fixtures.* import com.intellij.remoterobot.search.locators.Locator -import com.intellij.remoterobot.search.locators.byXpath +import com.intellij.remoterobot.fixtures.HeavyWeightWindowFixture import okhttp3.mockwebserver.MockResponse import org.junit.jupiter.api.* import org.junit.jupiter.api.extension.ExtendWith +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.Arguments +import org.junit.jupiter.params.provider.MethodSource +import workingset.auxiliary.components.elements.ButtonElement +import workingset.testutils.injectMemberList +import workingset.testutils.* +import java.time.Duration +import java.util.stream.Stream /** * Tests creating, editing and deleting working sets and masks from context menu. @@ -28,26 +33,36 @@ import org.junit.jupiter.api.extension.ExtendWith @TestMethodOrder(MethodOrderer.OrderAnnotation::class) @TestInstance(TestInstance.Lifecycle.PER_CLASS) @ExtendWith(RemoteRobotExtension::class) -class RenameDatasetTest { +class RenameDatasetTest : WorkingSetBase() { private var closableFixtureCollector = ClosableFixtureCollector() private var fixtureStack = mutableListOf() private var wantToClose = mutableListOf("Allocate DataSet Dialog", "Allocate Member Dialog") + override val connectionName = "con1" + override val wsName = "WS name" - private val projectName = "untitled" - private val connectionName = "con1" - private val wsName = "WS name" - private val errorHeader = "Error in plugin For Mainframe" private val errorType = "Unable to rename" private val pdsName = "$ZOS_USERID.UI.TEST".uppercase() + private val pdsMaskName = "$ZOS_USERID.UI.TEST*".uppercase() private val memberName = "TESTM" private val memberFinalName = "TESTMF" private val anotherMemberName = "TESTMA" - private val dsName = "$ZOS_USERID.UI.TESTD".uppercase() - private val dsFinalName = "$ZOS_USERID.UI.TESTDF".uppercase() + private val dsName = "$ZOS_USERID.UI.TESTD".uppercase() + private val dsFinalName = "$ZOS_USERID.UI.TESTDF".uppercase() private val anotherDsName = "$ZOS_USERID.UI.TESTA".uppercase() private var mapListDatasets = mutableMapOf() + private val dsFinalNameLong = "$ZOS_USERID.UI.TESTDF.123456789".uppercase() + private val tooLongString45 = "A".repeat(45) + + companion object { + @JvmStatic + fun valuesProviderMembers(): Stream { + return incorrectRenameMember.entries.stream().map { entry -> + Arguments.of(entry.key, entry.value) + } + } + } /** * Opens the project and Explorer, clears test environment. @@ -55,52 +70,36 @@ class RenameDatasetTest { @BeforeAll fun setUpAll(testInfo: TestInfo, remoteRobot: RemoteRobot) { startMockServer() - setUpTestEnvironment(projectName, fixtureStack, closableFixtureCollector, remoteRobot) - createValidConnectionWithMock( - testInfo, - connectionName, - projectName, - fixtureStack, - closableFixtureCollector, - remoteRobot - ) - createWsWithoutMask(projectName, wsName, connectionName, fixtureStack, closableFixtureCollector, remoteRobot) + setUpTestEnvironment(fixtureStack, closableFixtureCollector, remoteRobot) + createValidConnectionWithMock(testInfo, connectionName, fixtureStack, closableFixtureCollector, remoteRobot) + createWsWithoutMask(wsName, connectionName, fixtureStack, closableFixtureCollector, remoteRobot) + createMask(wsName, pdsMaskName, fixtureStack,closableFixtureCollector, ZOS_MASK, remoteRobot) + + okButton = ButtonElement(OK_TEXT, fixtureStack, remoteRobot) + } + + @BeforeEach + fun setUp(testInfo: TestInfo, remoteRobot: RemoteRobot){ + responseDispatcher.injectTestInfoForPdsDataset(testInfo.displayName, DATASET_FOR_RENAME_PROPERTY, pdsName) + + responseDispatcher.injectAllocationResultPo(PO_ORG_FULL, VB_RECORD_FORMAT_SHORT, dsName, PO_ORG_SHORT, 255) + responseDispatcher.injectAllocationResultPo(PO_ORG_FULL, VB_RECORD_FORMAT_SHORT, anotherDsName, PO_ORG_SHORT, 255) + + mapListDatasets[dsName] = listDS(dsName, PDS_TYPE, PO_ORG_SHORT) + mapListDatasets[anotherDsName] = listDS(anotherDsName, PDS_TYPE, PO_ORG_SHORT) + mapListDatasets[pdsName] = listDS(pdsName, PDS_TYPE, PO_ORG_SHORT) - responseDispatcher.injectEndpoint( - "${testInfo.displayName}_${pdsName}_restfiles", - { it?.requestLine?.contains("POST /zosmf/restfiles/ds/${pdsName}") ?: false }, - { MockResponse().setBody("{\"dsorg\":\"PO\",\"alcunit\":\"TRK\",\"primary\":10,\"secondary\":1,\"dirblk\":2,\"recfm\":\"VB\",\"blksize\":6120,\"lrecl\":255}") } - ) - allocatePDSAndCreateMask( - wsName, - pdsName, - projectName, - fixtureStack, - closableFixtureCollector, - remoteRobot, - "$ZOS_USERID.UI.TEST*", - directory = 2, - false - ) - mapListDatasets[pdsName] = listDS(pdsName, "PDS", "PO") - allocateDSWithMock(testInfo, dsName, remoteRobot) - allocateDSWithMock(testInfo, anotherDsName, remoteRobot) openWSAndListDatasets(testInfo, remoteRobot) - allocateMemberWithMock(testInfo, pdsName, memberName, remoteRobot) - allocateMemberWithMock(testInfo, pdsName, anotherMemberName, remoteRobot) } /** * Closes the project and clears test environment. */ @AfterAll - fun tearDownAll(testInfo: TestInfo, remoteRobot: RemoteRobot) = with(remoteRobot) { - deleteDatasetsWithMock(testInfo, remoteRobot) + fun tearDownAll(testInfo: TestInfo, remoteRobot: RemoteRobot) { mockServer.shutdown() - clearEnvironment(projectName, fixtureStack, closableFixtureCollector, remoteRobot) - ideFrameImpl(projectName, fixtureStack) { - close() - } + clearEnvironment(fixtureStack, closableFixtureCollector, remoteRobot) + return closeIntelligentProject(fixtureStack, remoteRobot) } /** @@ -108,6 +107,8 @@ class RenameDatasetTest { */ @AfterEach fun tearDown(remoteRobot: RemoteRobot) { + compressAndDecompressTree(wsName, fixtureStack, remoteRobot) + mapListDatasets.clear() responseDispatcher.removeAllEndpoints() closableFixtureCollector.closeWantedClosables(wantToClose, remoteRobot) } @@ -116,402 +117,170 @@ class RenameDatasetTest { * Tests renaming member when valid member name is provided. */ @Test - @Order(1) - fun testRenameMemberWithCorrectNameViaContextMenu(testInfo: TestInfo, remoteRobot: RemoteRobot) = - with(remoteRobot) { - var isFirstRequest = true - responseDispatcher.injectEndpoint( - "${testInfo.displayName}_restfiles_listmembers1", - { it?.requestLine?.contains("GET /zosmf/restfiles/ds/${pdsName}/member") ?: false && isFirstRequest }, - { MockResponse().setBody("{\"items\":[{\"member\": \"${memberName}\"},{\"member\": \"${anotherMemberName}\"}],\"returnedRows\": 2,\"JSONversion\": 1}") } - ) - responseDispatcher.injectEndpoint( - "${testInfo.displayName}_restfiles_listmembers2", - { it?.requestLine?.contains("GET /zosmf/restfiles/ds/${pdsName}/member") ?: false && !isFirstRequest }, - { MockResponse().setBody("{\"items\":[{\"member\": \"${memberFinalName}\"},{\"member\": \"${anotherMemberName}\"}],\"returnedRows\": 2,\"JSONversion\": 1}") } - ) - responseDispatcher.injectEndpoint( - "${testInfo.displayName}_restfiles_rename_member", - { it?.requestLine?.contains("PUT /zosmf/restfiles/ds/${pdsName}(${memberFinalName})") ?: false }, - { MockResponse().setBody("{\"request\":\"rename\",\"from-dataset\":{\"dsn\":\"${pdsName}\",\"member\":\"${memberName}\"}}") } - ) - ideFrameImpl(projectName, fixtureStack) { - explorer { - fileExplorer.click() - Thread.sleep(3000) - find(viewTree).findText(pdsName).doubleClick() - Thread.sleep(5000) - find(viewTree).findText(memberName).rightClick() - } - actionMenuItem(remoteRobot, "Rename").click() - dialog("Rename Member") { - find(byXpath("//div[@class='JBTextField']")).text = memberFinalName - isFirstRequest = false - } - clickButton("OK") - } - } + fun testRenameMemberWithCorrectNameViaContextMenu(testInfo: TestInfo, remoteRobot: RemoteRobot) { + var isFirstRequest = true + responseDispatcher.injectEndpoint( + "${testInfo.displayName}_restfiles_listmembers1", + { it?.requestLine?.contains("GET /zosmf/restfiles/ds/${pdsName}/member") ?: false && isFirstRequest }, + { MockResponse().setBody("{\"items\":[{\"member\": \"${memberName}\"},{\"member\": \"${anotherMemberName}\"}],\"returnedRows\": 2,\"JSONversion\": 1}") } + ) + responseDispatcher.injectEndpoint( + "${testInfo.displayName}_restfiles_listmembers2", + { it?.requestLine?.contains("GET /zosmf/restfiles/ds/${pdsName}/member") ?: false && !isFirstRequest }, + { MockResponse().setBody("{\"items\":[{\"member\": \"${memberFinalName}\"},{\"member\": \"${anotherMemberName}\"}],\"returnedRows\": 2,\"JSONversion\": 1}") } + ) + injectRenameMember(testInfo,pdsName,memberFinalName,memberName) + callRenameMemberPoint(fixtureStack,pdsName,memberName, remoteRobot) + newMemberNameInput(memberFinalName, fixtureStack, remoteRobot) + isFirstRequest = false + return clickByText(OK_TEXT, fixtureStack, remoteRobot) + } + // todo: 3 tests with the same scenario, create 1 test with parametrisation /** * Tests renaming member to name of another member in the same PDS and validates error pop-up notification. */ @Test - @Order(2) - fun testRenameMemberWithNameOfAnotherMemberViaContextMenu(testInfo: TestInfo, remoteRobot: RemoteRobot) = - with(remoteRobot) { - val errorDetail = "Member already exists" - responseDispatcher.injectEndpoint( - "${testInfo.displayName}_restfiles_rename_member", - { it?.requestLine?.contains("PUT /zosmf/restfiles/ds/${pdsName}(${anotherMemberName})") ?: false }, - { - MockResponse().setBody("{\"request\":\"rename\",\"from-dataset\":{\"dsn\":\"${pdsName}\",\"member\":\"${memberFinalName}\"}}") - .setResponseCode(500) - .setBody("{\"category\":\"4.0\",\"message\":\"Rename member failed\",\"rc\":\"4.0\",\"details\":[\"ISRZ002 $errorDetail - Directory already contains the specified member name.\"],\"reason\":\"0.0\"}") - } - ) - responseDispatcher.injectEndpoint( - "${testInfo.displayName}_restfiles_listmembers", - { it?.requestLine?.contains("GET /zosmf/restfiles/ds/${pdsName}/member") ?: false }, - { MockResponse().setBody("{\"items\":[{\"member\": \"${memberFinalName}\"},{\"member\": \"${anotherMemberName}\"}],\"returnedRows\": 2,\"JSONversion\": 1}") } - ) - ideFrameImpl(projectName, fixtureStack) { - explorer { - Thread.sleep(5000) - find(viewTree).findText(memberFinalName).rightClick() - } - actionMenuItem(remoteRobot, "Rename").click() - dialog("Rename Member") { - find(byXpath("//div[@class='JBTextField']")).text = anotherMemberName - } - clickButton("OK") - Thread.sleep(3000) - checkErrorNotification(errorHeader, errorType, errorDetail, projectName, fixtureStack, remoteRobot) - } - } - - /** - * Tests renaming member to the same name and validates error pop-up notification. - */ - @Test - @Order(3) - fun testRenameMemberWithTheSameNameViaContextMenu(testInfo: TestInfo, remoteRobot: RemoteRobot) = - with(remoteRobot) { - val errorDetail = "Member in use" - responseDispatcher.injectEndpoint( - "${testInfo.displayName}_restfiles_rename_member", - { it?.requestLine?.contains("PUT /zosmf/restfiles/ds/${pdsName}(${memberFinalName})") ?: false }, - { - MockResponse().setBody("{\"request\":\"rename\",\"from-dataset\":{\"dsn\":\"${pdsName}\",\"member\":\"${memberFinalName}\"}}") - .setResponseCode(500) - .setBody("{\"category\":\"4.0\",\"message\":\"Rename member failed\",\"rc\":\"12.0\",\"details\":[\"ISRZ002 $errorDetail - Member is being updated by you or another user. Enter HELP for a list of users using the data set.\"],\"reason\":\"0.0\"}") - } - ) - responseDispatcher.injectEndpoint( - "${testInfo.displayName}_restfiles_listmembers", - { it?.requestLine?.contains("GET /zosmf/restfiles/ds/${pdsName}/member") ?: false }, - { MockResponse().setBody("{\"items\":[{\"member\": \"${memberFinalName}\"},{\"member\": \"${anotherMemberName}\"}],\"returnedRows\": 2,\"JSONversion\": 1}") } - ) - ideFrameImpl(projectName, fixtureStack) { - explorer { - Thread.sleep(5000) - find(viewTree).findText(memberFinalName).rightClick() - } - actionMenuItem(remoteRobot, "Rename").click() - dialog("Rename Member") { - find(byXpath("//div[@class='JBTextField']")).text = memberFinalName - } - clickButton("OK") - Thread.sleep(3000) - checkErrorNotification(errorHeader, errorType, errorDetail, projectName, fixtureStack, remoteRobot) - } - } - - /** - * Tests renaming member to very long and validates error notification. - */ - @Test - @Order(4) - fun testRenameMemberWithTooLongNameViaContextMenu(remoteRobot: RemoteRobot) = with(remoteRobot) { - ideFrameImpl(projectName, fixtureStack) { - explorer { - Thread.sleep(5000) - find(viewTree).findText(memberFinalName).rightClick() - } - actionMenuItem(remoteRobot, "Rename").click() - dialog("Rename Member") { - find(byXpath("//div[@class='JBTextField']")).text = "123456789" - } - clickButton("OK") - find(byXpath("//div[@class='HeavyWeightWindow']")).findText( - MEMBER_NAME_LENGTH_MESSAGE - ) - Thread.sleep(3000) - clickButton("Cancel") - } + fun testRenameMemberWithNameOfAnotherMemberViaContextMenu(testInfo: TestInfo, remoteRobot: RemoteRobot) { + injectRenameMemberUnsuccessful(testInfo, pdsName, memberFinalName, anotherMemberName, 500, "4.0", MEMBER_ALREADY_EXISTS) + injectMemberList(testInfo, pdsName, listOf(memberFinalName, anotherMemberName)) + callRenameMemberPoint(fixtureStack,pdsName,memberFinalName, remoteRobot) + newMemberNameInput(anotherMemberName, fixtureStack, remoteRobot) + clickByText(OK_TEXT, fixtureStack, remoteRobot) + checkErrorNotification(RENAME_MEMBER_FAILED, RENAME_MEMBER_FAILED, MEMBER_ALREADY_EXISTS, fixtureStack, remoteRobot) + closeNotificztion(fixtureStack, remoteRobot) } /** - * Tests renaming member to invalid name and validates error notification. + * Tests renaming DataSet to name of another DataSet and validates error pop-up notification. */ @Test - @Order(5) - fun testRenameMemberWithInvalidNameViaContextMenu(remoteRobot: RemoteRobot) = with(remoteRobot) { - ideFrameImpl(projectName, fixtureStack) { - explorer { - Thread.sleep(5000) - find(viewTree).findText(memberFinalName).rightClick() - } - actionMenuItem(remoteRobot, "Rename").click() - dialog("Rename Member") { - find(byXpath("//div[@class='JBTextField']")).text = "@*" - } - clickButton("OK") - find(byXpath("//div[@class='HeavyWeightWindow']")).findText( - INVALID_MEMBER_NAME_MESSAGE - ) - Thread.sleep(3000) - clickButton("Cancel") - } + fun testRenameDatasetWithNameOfAnotherDatasetViaContextMenu(testInfo: TestInfo, remoteRobot: RemoteRobot) { + injectRenameDatasetUnsuccessful(testInfo, dsFinalName, anotherDsName, 500, RC_8, RC_8_TEXT) + injectListAllAllocatedDatasets("$ZOS_USERID.UI.TEST*".uppercase(),mapListDatasets) + callRenameDatasetPoint(fixtureStack, dsName, remoteRobot) + newDatasetNameInput(anotherDsName,fixtureStack,remoteRobot) + clickByText(OK_TEXT,fixtureStack,remoteRobot) + checkErrorNotification(DATA_SET_RENAME_FAILED_MSG, DATA_SET_RENAME_FAILED_MSG, DATA_SET_RENAME_FAILED, fixtureStack, remoteRobot) + closeNotificztion(fixtureStack, remoteRobot) } /** - * Tests renaming member to name with the invalid first symbol and validates error notification. + * Tests renaming member to the same name and validates error pop-up notification. */ @Test - @Order(6) - fun testRenameMemberWithInvalidFirstSymbolViaContextMenu(remoteRobot: RemoteRobot) = with(remoteRobot) { - ideFrameImpl(projectName, fixtureStack) { - explorer { - Thread.sleep(5000) - find(viewTree).findText(memberFinalName).rightClick() - } - actionMenuItem(remoteRobot, "Rename").click() - dialog("Rename Member") { - find(byXpath("//div[@class='JBTextField']")).text = "**" - } - clickButton("OK") - find(byXpath("//div[@class='HeavyWeightWindow']")).findText( - INVALID_MEMBER_NAME_BEGINNING_MESSAGE - ) - Thread.sleep(3000) - clickButton("Cancel") - } + fun testRenameMemberWithTheSameNameViaContextMenu(testInfo: TestInfo, remoteRobot: RemoteRobot) { + injectRenameMemberUnsuccessful(testInfo, pdsName, memberFinalName, memberFinalName, 500, "12.0", MEMBER_IN_USE) + injectMemberList(testInfo, pdsName, listOf(memberFinalName, anotherMemberName)) + callRenameMemberPoint(fixtureStack,pdsName,memberFinalName, remoteRobot) + newMemberNameInput(memberFinalName, fixtureStack, remoteRobot) + clickByText(OK_TEXT, fixtureStack, remoteRobot) + checkErrorNotification(RENAME_MEMBER_FAILED, RENAME_MEMBER_FAILED, MEMBER_IN_USE, fixtureStack, remoteRobot) + closeNotificztion(fixtureStack, remoteRobot) } /** - * Tests renaming member to empty name and validates error notification. + * Tests renaming member to the incorrect name and validates error pop-up notification. */ - @Test - @Order(7) - fun testRenameMemberWithEmptyNameViaContextMenu(remoteRobot: RemoteRobot) = with(remoteRobot) { - ideFrameImpl(projectName, fixtureStack) { - explorer { - Thread.sleep(5000) - find(viewTree).findText(memberFinalName).rightClick() - } - actionMenuItem(remoteRobot, "Rename").click() - dialog("Rename Member") { - find(byXpath("//div[@class='JBTextField']")).text = "" - } - clickButton("OK") - find(byXpath("//div[@class='HeavyWeightWindow']")).findText( - MEMBER_EMPTY_NAME_MESSAGE - ) - Thread.sleep(3000) - clickButton("Cancel") - } + @ParameterizedTest + @MethodSource("valuesProviderMembers") + fun testIncorrectRename(invalidName: String, errorMsg: String, testInfo: TestInfo, remoteRobot: RemoteRobot) = with(remoteRobot) { + injectRenameMemberUnsuccessful(testInfo, pdsName, memberFinalName, memberFinalName, 500, "12.0", MEMBER_IN_USE) + injectMemberList(testInfo, pdsName, listOf(memberFinalName, anotherMemberName)) + callRenameMemberPoint(fixtureStack,pdsName,memberFinalName,remoteRobot) + newMemberNameInput(invalidName, fixtureStack, remoteRobot) + clickByText(OK_TEXT, fixtureStack, remoteRobot) + + + val msgAll = find(messageLoc, Duration.ofSeconds(30) + ).findAllText() + var msg = "" + msgAll.forEach { msg += it.text } + + clickByText(CANCEL_TEXT, fixtureStack, remoteRobot) + Assertions.assertEquals(errorMsg, msg) } /** * Tests renaming DataSet when valid member name is provided. */ @Test - @Order(8) - fun testRenameDataSetWithCorrectNameViaContextMenu(testInfo: TestInfo, remoteRobot: RemoteRobot) = - with(remoteRobot) { - responseDispatcher.injectEndpoint( - "${testInfo.displayName}_restfiles_listmembers", - { it?.requestLine?.contains("GET /zosmf/restfiles/ds/${pdsName}/member") ?: false }, - { MockResponse().setBody("{\"items\":[{\"member\": \"${memberFinalName}\"},{\"member\": \"${anotherMemberName}\"}],\"returnedRows\": 2,\"JSONversion\": 1}") } - ) - responseDispatcher.injectEndpoint( - "${testInfo.displayName}_restfiles_rename_ds", - { it?.requestLine?.contains("PUT /zosmf/restfiles/ds/${dsFinalName}") ?: false }, - { MockResponse().setBody("{\"request\":\"rename\",\"from-dataset\":{\"dsn\":\"${dsName}\"}}") } - ) - mapListDatasets.remove(dsName) - mapListDatasets[dsFinalName] = listDS(dsFinalName, "PDS", "PO") - responseDispatcher.injectEndpoint( - "${testInfo.displayName}_restfiles", - { - it?.requestLine?.contains("GET /zosmf/restfiles/ds?dslevel=${"$ZOS_USERID.UI.TEST*".uppercase()}") - ?: false - }, - { MockResponse().setBody(buildFinalListDatasetJson()) } - ) - ideFrameImpl(projectName, fixtureStack) { - explorer { - Thread.sleep(3000) - find(viewTree).findText(dsName).rightClick() - } - actionMenuItem(remoteRobot, "Rename").click() - dialog("Rename Dataset") { - find(byXpath("//div[@class='JBTextField']")).text = dsFinalName - } - clickButton("OK") - } - } - - /** - * Tests renaming DataSet to name of another DataSet and validates error pop-up notification. - */ - @Test - @Order(9) - fun testRenameDatasetWithNameOfAnotherDatasetViaContextMenu(testInfo: TestInfo, remoteRobot: RemoteRobot) = - with(remoteRobot) { - val errorDetail = "data set rename failed" - responseDispatcher.injectEndpoint( - "${testInfo.displayName}_restfiles_rename_ds", - { it?.requestLine?.contains("PUT /zosmf/restfiles/ds/${anotherDsName}") ?: false }, - { - MockResponse().setBody("{\"request\":\"rename\",\"from-dataset\":{\"dsn\":\"${dsFinalName}\"}}") - .setResponseCode(500) - .setBody("{\"category\":\"1.0\",\"message\":\"$errorDetail\",\"rc\":\"8.0\",\"details\":[\"EDC5051I An error occurred when renaming a file.\"],\"reason\":\"6.0\"}") - } - ) - responseDispatcher.injectEndpoint( - "${testInfo.displayName}_restfiles_list_ds", - { - it?.requestLine?.contains("GET /zosmf/restfiles/ds?dslevel=${"$ZOS_USERID.UI.TEST*".uppercase()}") - ?: false - }, - { MockResponse().setBody(buildFinalListDatasetJson()) } - ) - ideFrameImpl(projectName, fixtureStack) { - explorer { - Thread.sleep(5000) - find(viewTree).findText(dsFinalName).rightClick() - } - actionMenuItem(remoteRobot, "Rename").click() - dialog("Rename Dataset") { - find(byXpath("//div[@class='JBTextField']")).text = anotherDsName - } - clickButton("OK") - Thread.sleep(3000) - checkErrorNotification(errorHeader, errorType, errorDetail, projectName, fixtureStack, remoteRobot) - } - } + fun testRenameDataSetWithCorrectNameViaContextMenu(testInfo: TestInfo, remoteRobot: RemoteRobot) { + injectRenameMember(testInfo, pdsName, memberFinalName, anotherMemberName) + injectMemberList(testInfo, pdsName, listOf(memberFinalName, anotherMemberName)) + injectRenameDataset(testInfo, dsFinalName, dsName) + mapListDatasets.remove(dsName) + mapListDatasets[dsFinalName] = listDS(dsFinalName, PDS_TYPE, PO_ORG_SHORT) + injectListAllAllocatedDatasets("$ZOS_USERID.UI.TEST*".uppercase(),mapListDatasets) + callRenameDatasetPoint(fixtureStack, dsName, remoteRobot) + newDatasetNameInput(dsFinalName,fixtureStack,remoteRobot) + okButton.click() + } /** * Tests renaming DataSet to name with invalid section and validates error notification. */ @Test - @Order(10) fun testRenameDatasetWithInvalidSectionViaContextMenu(remoteRobot: RemoteRobot) = with(remoteRobot) { - ideFrameImpl(projectName, fixtureStack) { - explorer { - Thread.sleep(5000) - find(viewTree).findText(dsFinalName).rightClick() - } - actionMenuItem(remoteRobot, "Rename").click() - dialog("Rename Dataset") { - find(byXpath("//div[@class='JBTextField']")).text = "$dsFinalName.123456789" - } - clickButton("OK") - var message = "" - find(byXpath("//div[@class='HeavyWeightWindow']")).findAllText().forEach { - message += it.text - } - Thread.sleep(3000) - clickButton("Cancel") - if (!message.contains(DATASET_INVALID_SECTION_MESSAGE)) { - throw Exception("Error message is different from expected") - } - } + callRenameDatasetPoint(fixtureStack, anotherDsName, remoteRobot) + newDatasetNameInput(dsFinalNameLong,fixtureStack,remoteRobot) + clickByText(OK_TEXT,fixtureStack,remoteRobot) + + val msgAll = find(messageLoc, Duration.ofSeconds(30) + ).findAllText() + var msg = "" + msgAll.forEach { msg += it.text } + + clickByText(CANCEL_TEXT, fixtureStack, remoteRobot) + Assertions.assertEquals(DATASET_INVALID_SECTION_MESSAGE, msg) } /** * Tests renaming DataSet to very long name and validates error notification. */ @Test - @Order(11) fun testRenameDatasetWithTooLongNameViaContextMenu(remoteRobot: RemoteRobot) = with(remoteRobot) { - ideFrameImpl(projectName, fixtureStack) { - explorer { - Thread.sleep(5000) - find(viewTree).findText(dsFinalName).rightClick() - } - actionMenuItem(remoteRobot, "Rename").click() - dialog("Rename Dataset") { - find(byXpath("//div[@class='JBTextField']")).text = "A".repeat(45) - } - clickButton("OK") - find(byXpath("//div[@class='HeavyWeightWindow']")).findText( - DATASET_NAME_LENGTH_MESSAGE - ) - Thread.sleep(3000) - clickButton("Cancel") - } + callRenameDatasetPoint(fixtureStack, anotherDsName, remoteRobot) + newDatasetNameInput(tooLongString45,fixtureStack,remoteRobot) + clickByText(OK_TEXT,fixtureStack,remoteRobot) + + val msgAll = find(messageLoc, Duration.ofSeconds(30) + ).findAllText() + var msg = "" + msgAll.forEach { msg += it.text } + + clickByText(CANCEL_TEXT, fixtureStack, remoteRobot) + Assertions.assertEquals(DATASET_NAME_LENGTH_MESSAGE, msg) } /** * Tests renaming DataSet to empty name and validates error notification. */ @Test - @Order(12) fun testRenameDatasetWithEmptyNameViaContextMenu(remoteRobot: RemoteRobot) = with(remoteRobot) { - ideFrameImpl(projectName, fixtureStack) { - explorer { - Thread.sleep(5000) - find(viewTree).findText(dsFinalName).rightClick() - } - actionMenuItem(remoteRobot, "Rename").click() - dialog("Rename Dataset") { - find(byXpath("//div[@class='JBTextField']")).text = "" - } - clickButton("OK") - Thread.sleep(3000) - find(byXpath("//div[@class='HeavyWeightWindow']")).findText( - MEMBER_EMPTY_NAME_MESSAGE - ) - clickButton("Cancel") - } - } + callRenameDatasetPoint(fixtureStack, anotherDsName, remoteRobot) + newDatasetNameInput(EMPTY_STRING,fixtureStack,remoteRobot) + clickByText(OK_TEXT,fixtureStack,remoteRobot) - private fun allocateDSWithMock(testInfo: TestInfo, dsName: String, remoteRobot: RemoteRobot) { - responseDispatcher.injectEndpoint( - "${testInfo.displayName}_${dsName}_restfiles", - { it?.requestLine?.contains("POST /zosmf/restfiles/ds/${dsName}") ?: false }, - { MockResponse().setBody("{\"dsorg\":\"PO\",\"alcunit\":\"TRK\",\"primary\":10,\"secondary\":1,\"dirblk\":1,\"recfm\":\"VB\",\"blksize\":6120,\"lrecl\":255}") } - ) - allocateDataSet(wsName, dsName, projectName, fixtureStack, remoteRobot) - mapListDatasets[dsName] = listDS(dsName, "PDS", "PO") + val msgAll = find(messageLoc, Duration.ofSeconds(30) + ).findAllText() + var msg = "" + msgAll.forEach { msg += it.text } + + clickByText(CANCEL_TEXT, fixtureStack, remoteRobot) + Assertions.assertEquals(MEMBER_EMPTY_NAME_MESSAGE, msg) } private fun openWSAndListDatasets(testInfo: TestInfo, remoteRobot: RemoteRobot) { responseDispatcher.injectEndpoint( "${testInfo.displayName}_restfiles", { - it?.requestLine?.contains("GET /zosmf/restfiles/ds?dslevel=${"$ZOS_USERID.UI.TEST*".uppercase()}") + it?.requestLine?.contains("GET /zosmf/restfiles/ds?dslevel=$pdsMaskName") ?: false }, { MockResponse().setBody(buildFinalListDatasetJson()) } ) - openOrCloseWorkingSetInExplorer(wsName, projectName, fixtureStack, remoteRobot) - } - - private fun allocateMemberWithMock( - testInfo: TestInfo, - dsName: String, - memberName: String, - remoteRobot: RemoteRobot - ) { - responseDispatcher.injectEndpoint( - "${testInfo.displayName}_${memberName}in${dsName}_restfiles", - { it?.requestLine?.contains("PUT /zosmf/restfiles/ds/${dsName}(${memberName})") ?: false }, - { MockResponse().setBody("{}") } - ) - allocateMemberForPDS(pdsName, memberName, projectName, fixtureStack, remoteRobot) + openOrCloseWorkingSetInExplorer(wsName, fixtureStack, remoteRobot) } private fun buildFinalListDatasetJson(): String { @@ -529,31 +298,4 @@ class RenameDatasetTest { } return result } - - private fun deleteDatasetsWithMock(testInfo: TestInfo, remoteRobot: RemoteRobot) { - val datasetsToBeDeleted = mutableListOf() - mapListDatasets.forEach { datasetsToBeDeleted.add(it.key) } - responseDispatcher.injectEndpoint( - "${testInfo.displayName}_restfiles_members", - { it?.requestLine?.contains("/member") ?: false }, - { MockResponse().setBody("{\"items\":[],\"returnedRows\":0,\"totalRows\":0,\"moreRows\":null,\"JSONversion\":1}") } - ) - datasetsToBeDeleted.forEach { entry -> - mapListDatasets.remove(entry) - responseDispatcher.injectEndpoint( - "${testInfo.displayName}_restfiles_${entry}", - { - it?.requestLine?.contains("GET /zosmf/restfiles/ds?dslevel=${"$ZOS_USERID.UI.TEST*".uppercase()}") - ?: false - }, - { MockResponse().setBody(buildFinalListDatasetJson()) } - ) - responseDispatcher.injectEndpoint( - "${testInfo.displayName}_delete_${entry}", - { it?.requestLine?.contains("DELETE /zosmf/restfiles/ds/${entry}") ?: false }, - { MockResponse().setBody("{}") } - ) - deleteDataset(entry, projectName, fixtureStack, remoteRobot) - } - } } diff --git a/src/uiTest/kotlin/workingset/ViewDatasetTest.kt b/src/uiTest/kotlin/workingset/ViewDatasetTest.kt index cd282a881..ac46e9928 100644 --- a/src/uiTest/kotlin/workingset/ViewDatasetTest.kt +++ b/src/uiTest/kotlin/workingset/ViewDatasetTest.kt @@ -12,33 +12,47 @@ package workingset import auxiliary.* import auxiliary.closable.ClosableFixtureCollector -import auxiliary.containers.* import com.intellij.remoterobot.RemoteRobot -import com.intellij.remoterobot.fixtures.ComponentFixture -import com.intellij.remoterobot.fixtures.dataExtractor.RemoteText import com.intellij.remoterobot.search.locators.Locator -import com.intellij.remoterobot.utils.keyboard -import okhttp3.mockwebserver.MockResponse import org.junit.jupiter.api.* import org.junit.jupiter.api.extension.ExtendWith -import java.awt.event.KeyEvent +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.MethodSource +import workingset.testutils.injectMemberContent +import org.junit.jupiter.params.provider.Arguments +import workingset.testutils.injectListAllAllocatedDatasetsWithContents +import workingset.testutils.injectPsDatasetContent +import workingset.testutils.injectSingleMember +import java.util.stream.Stream /** * Tests viewing dataset and members. */ @TestInstance(TestInstance.Lifecycle.PER_CLASS) @ExtendWith(RemoteRobotExtension::class) -class ViewDatasetTest { +class ViewDatasetTest :WorkingSetBase(){ private var closableFixtureCollector = ClosableFixtureCollector() private var fixtureStack = mutableListOf() private var wantToClose = mutableListOf("Add Working Set Dialog", "Create Mask Dialog") - private val projectName = "untitled" - private val connectionName = "valid connection" private var mapListDatasets = mutableMapOf() private var listMembersInDataset = mutableListOf() private val noItemsFoundMsg = "No items found" + private val wsNameWs1 = "WS1" + private val userIdPrefix = ZOS_USERID.uppercase() + private val emptyPdsDatasetNameTell = ".EMPTY.PDS".uppercase() + private val pdsDatasetName = "$ZOS_USERID.NONEMPTY.PDS".uppercase() + + companion object { + @JvmStatic + fun pairProvider(): Stream { + return Stream.of( + Arguments.of(WS_NAME_WS_2, EMPTY_MEMBER_CONTENT, ".EMPTY.PS"), + Arguments.of(WS_NAME_WS_3, SHORT_MEMBER_CONTENT, ".NONEMPTY.PS"), + ) + } + } /** * Opens the project and Explorer, clears test environment, creates valid connection. @@ -46,11 +60,10 @@ class ViewDatasetTest { @BeforeAll fun setUpAll(testInfo: TestInfo, remoteRobot: RemoteRobot) { startMockServer() - setUpTestEnvironment(projectName, fixtureStack, closableFixtureCollector, remoteRobot) + setUpTestEnvironment(fixtureStack, closableFixtureCollector, remoteRobot) createValidConnectionWithMock( testInfo, connectionName, - projectName, fixtureStack, closableFixtureCollector, remoteRobot @@ -62,19 +75,17 @@ class ViewDatasetTest { * Closes the project and clears test environment. */ @AfterAll - fun tearDownAll(remoteRobot: RemoteRobot) = with(remoteRobot) { + fun tearDownAll(remoteRobot: RemoteRobot) { mockServer.shutdown() - clearEnvironment(projectName, fixtureStack, closableFixtureCollector, remoteRobot) - ideFrameImpl(projectName, fixtureStack) { - close() - } + clearEnvironment(fixtureStack, closableFixtureCollector, remoteRobot) + closeIntelligentProject(fixtureStack, remoteRobot) } /** * Closes all unclosed closable fixtures that we want to close. */ @AfterEach - fun tearDown(remoteRobot: RemoteRobot) = with(remoteRobot) { + fun tearDown(remoteRobot: RemoteRobot) { closableFixtureCollector.closeWantedClosables(wantToClose, remoteRobot) responseDispatcher.removeAllEndpoints() mapListDatasets.clear() @@ -85,130 +96,44 @@ class ViewDatasetTest { * Test to view empty PDS dataset and check that File Explorer contains correct message. */ @Test - fun testViewEmptyPDS(testInfo: TestInfo, remoteRobot: RemoteRobot) = with(remoteRobot) { - val wsName = "WS1" - val datasetName = "$ZOS_USERID.EMPTY.PDS".uppercase() - createWsAndMaskWithMock(wsName, datasetName, true, "PDS", "PO", testInfo, remoteRobot) - openOrCloseWorkingSetInExplorer(wsName, projectName, fixtureStack, remoteRobot) + fun testViewEmptyPDS(testInfo: TestInfo, remoteRobot: RemoteRobot) { + createWsAndMaskWithMock(wsNameWs1, userIdPrefix+emptyPdsDatasetNameTell, EMPTY_MEMBER_CONTENT, PDS_TYPE, PO_ORG_SHORT, testInfo, remoteRobot) + openOrCloseWorkingSetInExplorer(wsNameWs1, fixtureStack, remoteRobot) findMessageInExplorer(noItemsFoundMsg, remoteRobot) - openOrCloseWorkingSetInExplorer(wsName, projectName, fixtureStack, remoteRobot) } /** - * Test to view empty PS dataset. + * Views empty/non-empty PS dataset. */ - @Test - fun testViewEmptyPS(testInfo: TestInfo, remoteRobot: RemoteRobot) = with(remoteRobot) { - val wsName = "WS2" - val datasetName = "$ZOS_USERID.EMPTY.PS".uppercase() - viewPSDataset(wsName, datasetName, true, testInfo, remoteRobot) + @ParameterizedTest + @MethodSource("pairProvider") + fun testViewPsDataset(wsName: String, memberContent:String, tell: String, testInfo: TestInfo, remoteRobot: RemoteRobot){ + val datasetName = userIdPrefix + tell + createWsAndMaskWithMock(wsName, datasetName, memberContent, EMPTY_STRING, SEQUENTIAL_ORG_SHORT, testInfo, remoteRobot) + openOrCloseWorkingSetInExplorer(wsName, fixtureStack, remoteRobot) + + injectPsDatasetContent(testInfo, datasetName, memberContent) + openTreesElement(datasetName, remoteRobot) + closeMemberOrDataset(remoteRobot) } - /** - * Test to view non-empty PS dataset. - */ - @Test - fun testViewNonEmptyPS(testInfo: TestInfo, remoteRobot: RemoteRobot) = with(remoteRobot) { - val wsName = "WS3" - val datasetName = "$ZOS_USERID.NONEMPTY.PS".uppercase() - viewPSDataset(wsName, datasetName, false, testInfo, remoteRobot) - } /** * Test to view non-empty PDS dataset and view empty/non-empty members. */ @Test - fun testViewNonEmptyPDS(testInfo: TestInfo, remoteRobot: RemoteRobot) = with(remoteRobot) { - val wsName = "WS4" - val datasetName = "$ZOS_USERID.NONEMPTY.PDS".uppercase() - createWsAndMaskWithMock(wsName, datasetName, false, "PDS", "PO", testInfo, remoteRobot) - openOrCloseWorkingSetInExplorer(wsName, projectName, fixtureStack, remoteRobot) - openMember(datasetName, "MEMBER1", true, testInfo, remoteRobot) - closeMemberOrDataset(remoteRobot) - openMember(datasetName, "MEMBER2", false, testInfo, remoteRobot) - closeMemberOrDataset(remoteRobot) - openOrCloseWorkingSetInExplorer(wsName, projectName, fixtureStack, remoteRobot) - } + fun testViewNonEmptyPDS(testInfo: TestInfo, remoteRobot: RemoteRobot) { + createWsAndMaskWithMock(WS_NAME_WS_4, pdsDatasetName, SHORT_MEMBER_CONTENT, PDS_TYPE, PO_ORG_SHORT, testInfo, remoteRobot) + openOrCloseWorkingSetInExplorer(WS_NAME_WS_4, fixtureStack, remoteRobot) - /** - * Views empty/non-empty PS dataset. - */ - private fun viewPSDataset( - wsName: String, - datasetName: String, - isEmpty: Boolean, - testInfo: TestInfo, - remoteRobot: RemoteRobot - ) = with(remoteRobot) { - createWsAndMaskWithMock(wsName, datasetName, isEmpty, "", "PS", testInfo, remoteRobot) - openOrCloseWorkingSetInExplorer(wsName, projectName, fixtureStack, remoteRobot) - openPSDataset(datasetName, isEmpty, testInfo, remoteRobot) + injectMemberContent(testInfo,pdsDatasetName, MEMBER_NAME_1) + openTreesElement(MEMBER_NAME_1, remoteRobot) closeMemberOrDataset(remoteRobot) - openOrCloseWorkingSetInExplorer(wsName, projectName, fixtureStack, remoteRobot) - } - - /** - * Closes opened dataset member or PS dataset. - */ - private fun closeMemberOrDataset(remoteRobot: RemoteRobot) = with(remoteRobot) { - ideFrameImpl(projectName, fixtureStack) { - with(textEditor()) { - keyboard { - hotKey(KeyEvent.VK_CONTROL, KeyEvent.VK_F4) - } - } - } - } - /** - * Opens PS dataset. - */ - private fun openPSDataset(datasetName: String, isEmpty: Boolean, testInfo: TestInfo, remoteRobot: RemoteRobot) = - with(remoteRobot) { - val psContent = if (isEmpty) { - "" - } else { - "content" - } - responseDispatcher.injectEndpoint( - "${testInfo.displayName}_restfiles_getcontent", - { it?.requestLine?.contains("GET /zosmf/restfiles/ds/-(TESTVOL)/$datasetName") ?: false }, - { MockResponse().setBody(psContent) } - ) - ideFrameImpl(projectName, fixtureStack) { - explorer { - find(viewTree).findAllText(datasetName).last().doubleClick() - Thread.sleep(2000) - } - } - } + injectMemberContent(testInfo,pdsDatasetName, MEMBER_NAME_2, "content") + openTreesElement(MEMBER_NAME_2, remoteRobot) + closeMemberOrDataset(remoteRobot) - /** - * Opens dataset member. - */ - private fun openMember( - datasetName: String, - memberName: String, - isEmpty: Boolean, - testInfo: TestInfo, - remoteRobot: RemoteRobot - ) = with(remoteRobot) { - val memberContent = if (isEmpty) { - "" - } else { - "content" - } - responseDispatcher.injectEndpoint( - "${testInfo.displayName}_restfiles_getmember", - { it?.requestLine?.contains("GET /zosmf/restfiles/ds/${datasetName}($memberName)") ?: false }, - { MockResponse().setBody(memberContent) } - ) - ideFrameImpl(projectName, fixtureStack) { - explorer { - find(viewTree).findAllText(memberName).last().doubleClick() - Thread.sleep(2000) - } - } } /** @@ -217,54 +142,24 @@ class ViewDatasetTest { private fun createWsAndMaskWithMock( wsName: String, datasetName: String, - isEmpty: Boolean, + memberContent: String, dsNtp: String, dsOrg: String, testInfo: TestInfo, remoteRobot: RemoteRobot - ) = with(remoteRobot) { - val maskList = listOf(Pair(datasetName, "z/OS")) + ) { + val maskList = listOf(Pair(datasetName, ZOS_MASK)) mapListDatasets[datasetName] = listDS(datasetName, dsNtp, dsOrg) - responseDispatcher.injectEndpoint( - "${testInfo.displayName}_restfiles", - { - it?.requestLine?.contains("GET /zosmf/restfiles/ds?dslevel=${datasetName}") - ?: false - }, - { MockResponse().setBody(buildResponseListJson(mapListDatasets, true)) } - ) - if (!isEmpty && dsNtp == "PDS") { + + injectListAllAllocatedDatasetsWithContents(testInfo, datasetName, mapListDatasets) + + if (memberContent != EMPTY_MEMBER_CONTENT && dsNtp == PDS_TYPE) { for (i in 1..5) { - listMembersInDataset.add("MEMBER$i") + listMembersInDataset.add(MEMBER_NAME_PATTERN+"$i") } - } - responseDispatcher.injectEndpoint( - "${testInfo.displayName}_restfiles_listmembers", - { it?.requestLine?.contains("GET /zosmf/restfiles/ds/${datasetName}/member") ?: false }, - { - MockResponse().setBody(buildListMembersJson()) } - ) - createWsAndMask(projectName, wsName, maskList, connectionName, fixtureStack, closableFixtureCollector, remoteRobot) + injectSingleMember(testInfo, datasetName, listMembersInDataset) + createWsAndMask(wsName, maskList, connectionName, fixtureStack, closableFixtureCollector, remoteRobot) } - private fun buildListMembersJson(): String { - var members = "[ " - if (listMembersInDataset.isNotEmpty()) { - listMembersInDataset.forEach { members += "{\"member\": \"${it}\"}," } - } - members = members.dropLast(1) + "]" - return "{\"items\":$members,\"returnedRows\": ${listMembersInDataset.size},\"JSONversion\": 1}" - } - - /** - * Checks if File Explorer contains expected message. - */ - private fun findMessageInExplorer(msg: String, remoteRobot: RemoteRobot) = with(remoteRobot) { - if (!find(viewTree).findAllText().map(RemoteText::text).joinToString("") - .contains(msg) - ) { - throw Exception("Expected message is not found") - } - } } \ No newline at end of file diff --git a/src/uiTest/kotlin/workingset/ViewWsPropertyTest.kt b/src/uiTest/kotlin/workingset/ViewWsPropertyTest.kt index 82b0344eb..07c97f130 100644 --- a/src/uiTest/kotlin/workingset/ViewWsPropertyTest.kt +++ b/src/uiTest/kotlin/workingset/ViewWsPropertyTest.kt @@ -12,14 +12,15 @@ package workingset import auxiliary.* import auxiliary.closable.ClosableFixtureCollector -import auxiliary.components.actionMenuItem import auxiliary.containers.* import com.intellij.remoterobot.RemoteRobot -import com.intellij.remoterobot.fixtures.ComponentFixture import com.intellij.remoterobot.search.locators.Locator import okhttp3.mockwebserver.MockResponse import org.junit.jupiter.api.* import org.junit.jupiter.api.extension.ExtendWith +import workingset.testutils.injectAllocateUssFile +import workingset.testutils.injectListAllUssFiles +import workingset.testutils.injectSingleSpecificMember /** * Tests viewing dataset and uss file properties. @@ -27,7 +28,7 @@ import org.junit.jupiter.api.extension.ExtendWith @TestMethodOrder(MethodOrderer.OrderAnnotation::class) @TestInstance(TestInstance.Lifecycle.PER_CLASS) @ExtendWith(RemoteRobotExtension::class) -class ViewWsPropertyTest { +class ViewWsPropertyTest : WorkingSetBase() { private val closableFixtureCollector = ClosableFixtureCollector() private val fixtureStack = mutableListOf() private val mapListDatasets = mutableMapOf() @@ -35,8 +36,8 @@ class ViewWsPropertyTest { private val mapListUssFiles = mutableMapOf() private val projectName = "untitled" - private val connectionName = "con1" - private val wsName = "WS name" + override val connectionName = "con1" + override val wsName = "WS name" private val pdsName = "${ZOS_USERID.uppercase()}.UI.TEST" private val memberName = "TESTM" private val dsName = "${ZOS_USERID.uppercase()}.UI.TESTD" @@ -48,35 +49,30 @@ class ViewWsPropertyTest { private val memList = "{\"member\":\"$memberName\",\"vers\":1,\"mod\":0,\"c4date\":\"2015/08/12\",\"m4date\":\"2015/08/12\",\"cnorc\":22,\n" + "\"inorc\":22,\"mnorc\":0,\"mtime\":\"05:48\",\"msec\":\"43\",\"user\":\"IBMUSER\",\"sclm\":\"N\"}," - private val fileList = "{\"name\":\"$ussFileName\",\"mode\":\"-rwxr--rw-\",\"size\":20,\"uid\":0,\"user\":\"${ZOS_USERID.uppercase()}\",\"gid\":1,\n" + - "\"group\":\"OMVSGRP\",\"mtime\":\"2015-11-24T02:12:04\"}," - private val dirList = "{\"name\":\"$ussDirName\",\"mode\":\"drwxr--rw-\",\"size\":888, \"uid\":0, \"user\":\"${ZOS_USERID.uppercase()}\",\"gid\":1,\n" + - "\"group\":\"OMVSGRP\",\"mtime\":\"2013-05-07T11:23:08\"}," - private val dirHereList = "{\"name\":\".\", \"mode\":\"drwxrwxrwx\", \"size\":8192, \"uid\":0, \"user\":\"${ZOS_USERID.uppercase()}\", \"gid\":1, \n" + - "\"group\":\"OMVSGRP\", \"mtime\":\"2015-11-24T02:12:04\"}," - private val dirParentList = "{\"name\":\"..\", \"mode\":\"drwxr-xr-x\", \"size\":8192, \"uid\":0, \"user\":\"${ZOS_USERID.uppercase()}\", \"gid\":1, \n" + - "\"group\":\"OMVSGRP\", \"mtime\":\"2015-09-15T02:38:29\"}," + private val fileList = + "{\"name\":\"$ussFileName\",\"mode\":\"-rwxr--rw-\",\"size\":20,\"uid\":0,\"user\":\"${ZOS_USERID.uppercase()}\",\"gid\":1,\n" + + "\"group\":\"OMVSGRP\",\"mtime\":\"2015-11-24T02:12:04\"}," + private val dirList = + "{\"name\":\"$ussDirName\",\"mode\":\"drwxr--rw-\",\"size\":888, \"uid\":0, \"user\":\"${ZOS_USERID.uppercase()}\",\"gid\":1,\n" + + "\"group\":\"OMVSGRP\",\"mtime\":\"2013-05-07T11:23:08\"}," + private val dirHereList = + "{\"name\":\".\", \"mode\":\"drwxrwxrwx\", \"size\":8192, \"uid\":0, \"user\":\"${ZOS_USERID.uppercase()}\", \"gid\":1, \n" + + "\"group\":\"OMVSGRP\", \"mtime\":\"2015-11-24T02:12:04\"}," + private val dirParentList = + "{\"name\":\"..\", \"mode\":\"drwxr-xr-x\", \"size\":8192, \"uid\":0, \"user\":\"${ZOS_USERID.uppercase()}\", \"gid\":1, \n" + + "\"group\":\"OMVSGRP\", \"mtime\":\"2015-09-15T02:38:29\"}," /** * Opens the project and Explorer, clears test environment, creates working set with dataset and uss masks, * allocates dataset and pds with member, creates uss file and directory. */ @BeforeAll - fun setUpAll(testInfo: TestInfo, remoteRobot: RemoteRobot) = with(remoteRobot) { + fun setUpAll(testInfo: TestInfo, remoteRobot: RemoteRobot) { startMockServer() - responseDispatcher.injectEndpoint( - "${testInfo.displayName}_info", - { it?.requestLine?.contains("zosmf/info") ?: false }, - { MockResponse().setBody(responseDispatcher.readMockJson("infoResponse") ?: "") } - ) - responseDispatcher.injectEndpoint( - "${testInfo.displayName}_resttopology", - { it?.requestLine?.contains("zosmf/resttopology/systems") ?: false }, - { MockResponse().setBody(responseDispatcher.readMockJson("infoResponse") ?: "") } - ) - setUpTestEnvironment(projectName, fixtureStack, closableFixtureCollector, remoteRobot) + responseDispatcher.injectTestInfo(testInfo) + responseDispatcher.injectTestInfoRestTopology(testInfo) + setUpTestEnvironment(fixtureStack, closableFixtureCollector, remoteRobot) createConnection( - projectName, fixtureStack, closableFixtureCollector, connectionName, @@ -84,7 +80,7 @@ class ViewWsPropertyTest { remoteRobot, "https://${mockServer.hostName}:${mockServer.port}" ) - createWsWithoutMask(projectName, wsName, connectionName, fixtureStack, closableFixtureCollector, remoteRobot) + createWsWithoutMask(wsName, connectionName, fixtureStack, closableFixtureCollector, remoteRobot) responseDispatcher.injectEndpoint( "listAllAllocatedDatasets_restfiles", { it?.requestLine?.contains("GET /zosmf/restfiles/ds?dslevel=${maskName}") ?: false }, @@ -97,201 +93,187 @@ class ViewWsPropertyTest { ) mapListUssFiles["."] = dirHereList mapListUssFiles[".."] = dirParentList - responseDispatcher.injectEndpoint( - "listAllUssFiles_restfiles", - { it?.requestLine?.contains("GET /zosmf/restfiles/fs?path") ?: false }, - { MockResponse().setBody(buildResponseListJson(mapListUssFiles, true)) } - ) - responseDispatcher.injectEndpoint( - "allocateDatasetMember_restfiles", - { it?.requestLine?.contains("PUT /zosmf/restfiles/ds/$pdsName($memberName)") ?: false }, - { MockResponse().setResponseCode(204) } + injectListAllUssFiles(mapListUssFiles) + injectSingleSpecificMember(pdsName, memberName) + responseDispatcher.injectAllocationResultPds(pdsName) + responseDispatcher.injectAllocationResultPo( + SEQUENTIAL_ORG_SHORT, + TRACKS_ALLOCATION_UNIT_SHORT, + dsName, + VB_RECORD_FORMAT_SHORT, + 255 ) - responseDispatcher.injectEndpoint( - "allocatePds_restfiles", - { it?.requestLine?.contains("POST /zosmf/restfiles/ds/$pdsName") ?: false }, - { MockResponse().setBody("{\"dsorg\":\"PDS\",\"alcunit\":\"TRK\",\"primary\":10,\"secondary\":1,\"dirblk\":2,\"recfm\":\"VB\",\"blksize\":6120,\"lrecl\":255}") } - ) - responseDispatcher.injectEndpoint( - "allocateDs_restfiles", - { it?.requestLine?.contains("POST /zosmf/restfiles/ds/$dsName") ?: false }, - { MockResponse().setBody("{\"dsorg\":\"PS\",\"alcunit\":\"TRK\",\"primary\":10,\"secondary\":1,\"dirblk\":0,\"recfm\":\"VB\",\"blksize\":6120,\"lrecl\":255}") } - ) - responseDispatcher.injectEndpoint( - "allocateUssFile_restfiles", - { it?.requestLine?.contains("POST /zosmf/restfiles/fs$ussMaskName/$ussFileName") ?: false }, - { MockResponse().setResponseCode(201) } - ) - responseDispatcher.injectEndpoint( - "allocateUssDir_restfiles", - { it?.requestLine?.contains("POST /zosmf/restfiles/fs$ussMaskName/$ussDirName") ?: false }, - { MockResponse().setResponseCode(201) } - ) - mapListDatasets[pdsName] = listDS(pdsName, "PDS", "PO") + injectAllocateUssFile(ussMaskName, ussFileName) + injectAllocateUssFile(ussMaskName, ussDirName) + mapListDatasets[pdsName] = listDS(pdsName, PDS_TYPE, PO_ORG_SHORT) allocatePDSAndCreateMask( wsName, pdsName, - projectName, fixtureStack, closableFixtureCollector, remoteRobot, maskName, directory = 2 ) - ideFrameImpl(projectName, fixtureStack) { - createMask(wsName, fixtureStack, closableFixtureCollector) - createMaskDialog(fixtureStack) { - createMask(Pair(ussMaskName, "USS")) - clickButton("OK") - Thread.sleep(3000) - } - } - mapListDatasets[dsName] = listDS(dsName, "", "PS") - allocateDataSet(wsName, dsName, projectName, fixtureStack, remoteRobot) + createMask(wsName, ussMaskName, fixtureStack, closableFixtureCollector, USS_MASK, remoteRobot) + mapListDatasets[dsName] = listDS(dsName, "", SEQUENTIAL_ORG_SHORT) + allocateDataSet(wsName, dsName, fixtureStack, remoteRobot) mapListDatasetMembers[memberName] = memList - allocateMemberForPDS(pdsName, memberName, projectName, fixtureStack, remoteRobot) - createUssFile(ussMaskName, ussFileName, UssFileType.File, projectName, fixtureStack, remoteRobot) + allocateMemberForPDS(pdsName, memberName, fixtureStack, remoteRobot) mapListUssFiles[ussFileName] = fileList - createUssFile(ussMaskName, ussDirName, UssFileType.Directory, projectName, fixtureStack, remoteRobot) mapListUssFiles[ussDirName] = dirList - ideFrameImpl(projectName, fixtureStack) { - explorer { - fileExplorer.click() - find(viewTree).findText(pdsName).doubleClick() - Thread.sleep(1000) - find(viewTree).findText(ussMaskName).doubleClick() - Thread.sleep(1000) - } - } + openTreesElement(pdsName, remoteRobot) + openTreesElement(ussMaskName, remoteRobot) } /** * Closes the project and clears test environment. */ @AfterAll - fun tearDownAll(remoteRobot: RemoteRobot) = with(remoteRobot) { + fun tearDownAll(remoteRobot: RemoteRobot) { mockServer.shutdown() - - clearEnvironment(projectName, fixtureStack, closableFixtureCollector, remoteRobot) - ideFrameImpl(projectName, fixtureStack) { - close() - } + clearEnvironment(fixtureStack, closableFixtureCollector, remoteRobot) + closeIntelligentProject(fixtureStack, remoteRobot) } /** * Test to check if member properties in opened dialog and expected values are matching */ @Test - @Order(1) - fun testViewMemberProperties(remoteRobot: RemoteRobot) = with(remoteRobot) { - ideFrameImpl(projectName, fixtureStack) { - explorer { - fileExplorer.click() - Thread.sleep(1000) - find(viewTree).findText(memberName).rightClick() - } - actionMenuItem(remoteRobot, "Properties").click() - memberPropertiesDialog(fixtureStack) { - if (!areMemberPropertiesValid(memberName, "1.0", "2015/08/12", "2015/08/12", - "05:48", "IBMUSER", "22", "22", "0")) { - throw Exception("Properties in opened 'Member Properties' dialog are different from expected") - } - clickButton("OK") - } - } + fun testViewMemberProperties(remoteRobot: RemoteRobot) { + callTreesElementProperty(memberName, fixtureStack, remoteRobot) + val memberPropertiesValid = isMemberPropertyValid( + memberName, + "1.0", + "2015/08/12", + "2015/08/12", + "05:48", + "IBMUSER", + "22", + "22", + "0", + remoteRobot + ) + clickByText(OK_TEXT, fixtureStack, remoteRobot) + Assertions.assertTrue(memberPropertiesValid) } /** * Test to check if dataset properties in opened dialog and expected values are matching */ @Test - @Order(2) - fun testViewDatasetProperties(remoteRobot: RemoteRobot) = with(remoteRobot) { - ideFrameImpl(projectName, fixtureStack) { - explorer { - fileExplorer.click() - Thread.sleep(1000) - find(viewTree).findText(dsName).rightClick() - } - actionMenuItem(remoteRobot, "Properties").click() - datasetPropertiesDialog(fixtureStack) { - if (!areDatasetPropertiesValid(dsName, "", "TEST.CATALOG.MASTER", "TESTVOL", - "3390", "PS", "VB", "255", "3200", "10", - "TRACKS", "1", "1", "2021/11/15", "2021/11/17", "***None***")) { - throw Exception("Properties in opened 'Dataset Properties' dialog are different from expected") - } - clickButton("OK") - } - } + fun testViewDatasetProperties(remoteRobot: RemoteRobot) { + openPropertyDatasetName(dsName, fixtureStack, remoteRobot) + val datasetPropertyValid = isDatasetPropertyValid( + dsName, "", "TEST.CATALOG.MASTER", "TESTVOL", + "3390", "Sequential (PS)", "VB", "255", "3200", "10", + "TRACKS", "1", "1", "2021/11/15", "2021/11/17", "***None***", remoteRobot + ) + clickByText(OK_TEXT, fixtureStack, remoteRobot) + Assertions.assertTrue(datasetPropertyValid) } /** * Test to check if pds properties in opened dialog and expected values are matching */ @Test - @Order(3) - fun testViewPdsProperties(remoteRobot: RemoteRobot) = with(remoteRobot) { - ideFrameImpl(projectName, fixtureStack) { - explorer { - fileExplorer.click() - Thread.sleep(1000) - find(viewTree).findText(pdsName).rightClick() - } - actionMenuItem(remoteRobot, "Properties").click() - datasetPropertiesDialog(fixtureStack) { - if (!areDatasetPropertiesValid(pdsName, "PDS", "TEST.CATALOG.MASTER", "TESTVOL", - "3390", "PO", "VB", "255", "3200", "10", - "TRACKS", "1", "1", "2021/11/15", "2021/11/17", "***None***")) { - throw Exception("Properties in opened 'Dataset Properties' dialog are different from expected") - } - clickButton("OK") - } - } + fun testViewPdsProperties(remoteRobot: RemoteRobot) { + openPropertyDatasetName(pdsName, fixtureStack, remoteRobot) + val datasetPropertyValid = isDatasetPropertyValid( + pdsName, "PDS", "TEST.CATALOG.MASTER", "TESTVOL", + "3390", PO_ORG_FULL, "VB", "255", "3200", "10", + "TRACKS", "1", "1", "2021/11/15", "2021/11/17", "***None***", remoteRobot + ) + clickByText(OK_TEXT, fixtureStack, remoteRobot) + Assertions.assertTrue(datasetPropertyValid) } /** * Test to check if uss file properties in opened dialog and expected values are matching */ @Test - @Order(4) - fun testViewUssFileProperties(remoteRobot: RemoteRobot) = with(remoteRobot) { - ideFrameImpl(projectName, fixtureStack) { - explorer { - fileExplorer.click() - Thread.sleep(1000) - find(viewTree).findText(ussFileName).rightClick() - } - actionMenuItem(remoteRobot, "Properties").click() - ussFilePropertiesDialog(fixtureStack) { - if (!areUssFilePropertiesValid(ussFileName, ussMaskName.drop(1), "$ussMaskName/$ussFileName", "20 bytes", - "2015-11-24T02:12:04", "USER", "OMVSGRP", "1", "READ_WRITE_EXECUTE", "READ", "READ_WRITE")) { - throw Exception("Properties in opened 'File Properties' dialog are different from expected") - } - clickButton("OK") - } - } + fun testViewUssFileProperties(remoteRobot: RemoteRobot) { + openPropertyDatasetName(ussFileName, fixtureStack, remoteRobot) + val validPropertyStatus = isUssFilePropertyValid( + ussFileName, ussMaskName.drop(1), "$ussMaskName/$ussFileName", "20 bytes", + "2015-11-24T02:12:04", ZOS_USERID.uppercase(), "OMVSGRP", "1", + "READ_WRITE_EXECUTE", "READ", "READ_WRITE", remoteRobot + ) + clickByText(OK_TEXT, fixtureStack, remoteRobot) + Assertions.assertTrue(validPropertyStatus) } /** * Test to check if uss directory properties in opened dialog and expected values are matching */ @Test - @Order(5) - fun testViewUssDirProperties(remoteRobot: RemoteRobot) = with(remoteRobot) { - ideFrameImpl(projectName, fixtureStack) { - explorer { - fileExplorer.click() - Thread.sleep(1000) - find(viewTree).findText(ussDirName).rightClick() + fun testViewUssDirProperties(remoteRobot: RemoteRobot) { + openPropertyDatasetName(ussDirName, fixtureStack, remoteRobot) + val validPropertyStatus = isUssFilePropertyValid( + ussDirName, ussMaskName.drop(1), + "$ussMaskName/$ussDirName", "888 bytes", "2013-05-07T11:23:08", ZOS_USERID.uppercase(), + "OMVSGRP", "1", "READ_WRITE_EXECUTE", "READ", "READ_WRITE", + remoteRobot + ) + clickByText(OK_TEXT, fixtureStack, remoteRobot) + Assertions.assertTrue(validPropertyStatus) + } + + private fun isMemberPropertyValid( + memName: String, + version: String, + createDate: String, + modDate: String, + modTime: String, + userId: String, + curRecNum: String, + begRecNum: String, + changedRecNum: String, + remoteRobot: RemoteRobot, + ): Boolean = with(remoteRobot) { + var isValid = false + ideFrameImpl(PROJECT_NAME, fixtureStack) { + memberPropertiesDialog(fixtureStack) { + isValid = areMemberPropertiesValid( + memName, version, createDate, modDate, modTime, userId, curRecNum, begRecNum, changedRecNum + ) } - actionMenuItem(remoteRobot, "Properties").click() + } + return isValid + } + + private fun isDatasetPropertyValid( + dsName: String, dsNameType: String, catalogName: String, volumeSerials: String, deviceType: String, + organization: String, recordFormat: String, recordLength: String, blockSize: String, sizeInTracks: String, + spaceUnits: String, usedTracks: String, usedExtents: String, createDate: String, modDate: String, + expirationDate: String, remoteRobot: RemoteRobot, + ): Boolean = with(remoteRobot) { + var isValid = false + ideFrameImpl(PROJECT_NAME, fixtureStack) { + datasetPropertiesDialog(fixtureStack) { + isValid = areDatasetPropertiesValid( + dsName, dsNameType, catalogName, volumeSerials, deviceType, organization, recordFormat, + recordLength, blockSize, sizeInTracks, spaceUnits, usedTracks, usedExtents, createDate, modDate, + expirationDate + ) + } + } + return isValid + } + + private fun isUssFilePropertyValid( + fileName: String, location: String, path: String, size: String, modDate: String, + owner: String, group: String, groupId: String, ownerPerm: String, groupPerm: String, + allPerm: String, remoteRobot: RemoteRobot, + ): Boolean = with(remoteRobot) { + var isValid = false + ideFrameImpl(PROJECT_NAME, fixtureStack) { ussFilePropertiesDialog(fixtureStack) { - if (!areUssFilePropertiesValid(ussDirName, ussMaskName.drop(1), "$ussMaskName/$ussDirName", "888 bytes", - "2013-05-07T11:23:08", "USER", "OMVSGRP", "1", "READ_WRITE_EXECUTE", "READ", "READ_WRITE")) { - throw Exception("Properties in opened 'Directory Properties' dialog are different from expected") - } - clickButton("OK") + isValid = areUssFilePropertiesValid( + fileName, location, path, size, modDate, owner, group, groupId, ownerPerm, groupPerm, allPerm + ) } } + return isValid } } \ No newline at end of file diff --git a/src/uiTest/kotlin/workingset/WorkingSetBase.kt b/src/uiTest/kotlin/workingset/WorkingSetBase.kt index 554e9aa7f..1d34d7bde 100644 --- a/src/uiTest/kotlin/workingset/WorkingSetBase.kt +++ b/src/uiTest/kotlin/workingset/WorkingSetBase.kt @@ -1,5 +1,3 @@ -package workingset - /* * This program and the accompanying materials are made available under the terms of the * Eclipse Public License v2.0 which accompanies this distribution, and is available at @@ -9,6 +7,9 @@ package workingset * * Copyright IBA Group 2020 */ +package workingset + + import auxiliary.* import auxiliary.closable.ClosableFixtureCollector @@ -18,10 +19,16 @@ import auxiliary.containers.* import com.intellij.remoterobot.RemoteRobot import com.intellij.remoterobot.fixtures.ComponentFixture import com.intellij.remoterobot.fixtures.ContainerFixture +import com.intellij.remoterobot.fixtures.HeavyWeightWindowFixture +import com.intellij.remoterobot.fixtures.JTextFieldFixture +import com.intellij.remoterobot.fixtures.dataExtractor.RemoteText import com.intellij.remoterobot.search.locators.Locator -import com.intellij.remoterobot.search.locators.byXpath +import com.intellij.remoterobot.utils.keyboard import org.junit.jupiter.api.* import org.junit.jupiter.api.extension.ExtendWith +import workingset.auxiliary.components.dialogs.* +import workingset.auxiliary.components.elements.ButtonElement +import java.awt.event.KeyEvent /** * Tests allocating datasets with valid and invalid inputs. @@ -31,15 +38,29 @@ import org.junit.jupiter.api.extension.ExtendWith open class WorkingSetBase { private var closableFixtureCollector = ClosableFixtureCollector() private var fixtureStack = mutableListOf() - private var mapListDatasets = mutableMapOf() - private val projectName = "untitled" open val connectionName = "valid connection" - // - private val wsName = "WS1" + var okButton: ButtonElement = ButtonElement() + var yesButton: ButtonElement = ButtonElement() + var cancelButton: ButtonElement = ButtonElement() + + open val wsName = "WS1" internal open val datasetName = "$ZOS_USERID.ALLOC." - internal fun buildDatasetConfigString(dsName: String, dsntp: String, datasetOrganization:String, recordLength:Int, recordFormatShort:String): String{ + var editWorkingSetSubDialog: EditWorkingSetSubDialog = EditWorkingSetSubDialog() + var createMaskSubDialog: CreateMaskSubDialog = CreateMaskSubDialog() + var renameDatasetMaskDialog: RenameDatasetMaskDialog = RenameDatasetMaskDialog() + var deletionOfDSMask: DeletionOfDSMask = DeletionOfDSMask() + var deletionOfUssPathRoot: DeletionOfUssPathRoot = DeletionOfUssPathRoot() + var addWorkingSetDialog = AddWorkingSetSubDialog() + + internal fun buildDatasetConfigString( + dsName: String, + dsntp: String, + datasetOrganization: String, + recordLength: Int, + recordFormatShort: String + ): String { return "{\n" + " \"dsname\": \"${dsName}\",\n" + " \"blksz\": \"3200\",\n" + @@ -64,36 +85,6 @@ open class WorkingSetBase { " }," } - internal fun allocateDataSetWithDefaultConfig( - wsName: String, - datasetName: String, - projectName: String, - fixtureStack: MutableList, - remoteRobot: RemoteRobot - ) = with(remoteRobot) { - ideFrameImpl(projectName, fixtureStack) { - explorer { - fileExplorer.click() - find(viewTree).findText(wsName).rightClick() - } - actionMenu(remoteRobot, NEW_POINT_TEXT).click() - actionMenuItem(remoteRobot, DATASET_POINT_TEXT).click() - allocateDatasetDialog(fixtureStack) { - allocateDataset(datasetName, PARTITIONED_EXTENDED_NAME_FULL, "TRK", 10, 1, 0, "VB", 255, 6120) - clickButton(OK_TEXT) -// Thread.sleep(10000) - } - find(myDialogXpathLoc).findText(datasetHasBeenCreated.format(datasetName)) - clickButton(NO_TEXT) - explorer { - fileExplorer.click() - find(viewTree).findText(wsName).rightClick() - } - actionMenuItem(remoteRobot, REFRESH_POINT_TEXT).click() -// Thread.sleep(3000) - } - } - fun listDS(dsName: String, dsNtp: String, dsOrg: String): String { return "{\n" + " \"dsname\": \"${dsName}\",\n" + @@ -118,46 +109,49 @@ open class WorkingSetBase { " \"vols\": \"TESTVOL\"\n" + " }," } + internal fun allocateDataSet( - wsName: String, - datasetName: String, - datasetOrganization: String, - allocationUnit: String, - primaryAllocation: Int, - secondaryAllocation: Int, - directory: Int, - recordFormat: String, - recordLength: Int, - blockSize: Int, - averageBlockLength: Int, - remoteRobot: RemoteRobot, + wsName: String, + datasetName: String, + datasetOrganization: String, + allocationUnit: String, + primaryAllocation: Int, + secondaryAllocation: Int, + directory: Int, + recordFormat: String, + recordLength: Int, + blockSize: Int, + averageBlockLength: Int, + remoteRobot: RemoteRobot, ) = with(remoteRobot) { - ideFrameImpl(projectName, fixtureStack) { + ideFrameImpl(PROJECT_NAME, fixtureStack) { explorer { fileExplorer.click() find(viewTree).findText(wsName).rightClick() } - actionMenu(remoteRobot, "New").click() - actionMenuItem(remoteRobot, "Dataset").click() + actionMenu(remoteRobot, NEW_POINT_TEXT).click() + actionMenuItem(remoteRobot, DATASET_POINT_TEXT).click() allocateDatasetDialog(fixtureStack) { allocateDataset( - datasetName, - datasetOrganization, - allocationUnit, - primaryAllocation, - secondaryAllocation, - directory, - recordFormat, - recordLength, - blockSize, - averageBlockLength + datasetName, + datasetOrganization, + allocationUnit, + primaryAllocation, + secondaryAllocation, + directory, + recordFormat, + recordLength, + blockSize, + averageBlockLength ) - clickButton("OK") - find(myDialogXpathLoc).findText("Dataset $datasetName Has Been ") - clickButton("No") -// closableFixtureCollector.closeOnceIfExists(AllocateDatasetDialog.name) + clickByText(OK_TEXT, fixtureStack, remoteRobot) + + closableFixtureCollector.closeOnceIfExists(AllocateDatasetDialog.name) } +// Thread.sleep(3000) +// find(myDialogXpathLoc).findText("Dataset $datasetName Has Been ") +// clickButton("No") } } @@ -166,18 +160,18 @@ open class WorkingSetBase { * Creates working set and z/OS mask. */ internal fun createWsAndMask(remoteRobot: RemoteRobot) = with(remoteRobot) { - createWsWithoutMask(projectName, wsName, connectionName, fixtureStack, closableFixtureCollector, remoteRobot) - ideFrameImpl(projectName, fixtureStack) { + createWsWithoutMask(wsName, connectionName, fixtureStack, closableFixtureCollector, remoteRobot) + ideFrameImpl(PROJECT_NAME, fixtureStack) { createMask(wsName, fixtureStack, closableFixtureCollector) createMaskDialog(fixtureStack) { - createMask(Pair("$datasetName*", "z/OS")) - clickButton("OK") + createMask(Pair("$datasetName*", ZOS_MASK)) + clickByText(OK_TEXT, fixtureStack, remoteRobot) } closableFixtureCollector.closeOnceIfExists(CreateMaskDialog.name) } } - internal fun buildFinalListDatasetJson(): String { + internal fun buildFinalListDatasetJson(mapListDatasets: MutableMap): String { var result = "{}" if (mapListDatasets.isNotEmpty()) { var listDatasetsJson = "{\"items\":[" @@ -192,4 +186,369 @@ open class WorkingSetBase { } return result } -} \ No newline at end of file + + private fun openDatasetProperty(datasetName: String, remoteRobot: RemoteRobot) = with(remoteRobot) { + ideFrameImpl(PROJECT_NAME, fixtureStack) { + explorer { + fileExplorer.click() + find(viewTree).findText(datasetName).rightClick() + } + } + } + + internal fun migrateDataset( + datasetName: String, + migratedDs: String, + mapListDatasets: MutableMap, + fixtureStack: MutableList, + remoteRobot: RemoteRobot + ) = with(remoteRobot) { + openDatasetProperty(datasetName, remoteRobot) + responseDispatcher.injectMigratePdsRestFiles(datasetName, HMIGRATE_MIGRATE_OPTIONS) + mapListDatasets[datasetName] = migratedDs + ideFrameImpl(PROJECT_NAME, fixtureStack) { + actionMenuItem(remoteRobot, MIGRATE_POINT_TEXT).click() + } + } + + internal fun recallDataset( + datasetName: String, + fixtureStack: MutableList, + mapListDatasets: MutableMap, + remoteRobot: RemoteRobot + ) = with(remoteRobot) { + openDatasetProperty(datasetName, remoteRobot) + responseDispatcher.injectRecallPds(datasetName) + mapListDatasets[datasetName] = listDS(datasetName, PDS_TYPE, PO_ORG_SHORT) + ideFrameImpl(PROJECT_NAME, fixtureStack) { + actionMenuItem(remoteRobot, RECALL_POINT_TEXT).click() + } + } + + internal fun callRenameDatasetPoint( + fixtureStack: MutableList, + datasetName: String, + remoteRobot: RemoteRobot + ) = with(remoteRobot) { + ideFrameImpl(PROJECT_NAME, fixtureStack) { + explorer { + Thread.sleep(3000) + find(viewTree).findText(datasetName).rightClick() + } + actionMenuItem(remoteRobot, RENAME_POINT_TEXT).click() + } + } + + internal fun callRenameMemberPoint( + fixtureStack: MutableList, + datasetName: String, + memberName: String, + remoteRobot: RemoteRobot + ) = with(remoteRobot) { + ideFrameImpl(PROJECT_NAME, fixtureStack) { + explorer { + fileExplorer.click() + find(viewTree).findText(datasetName).doubleClick() + find(viewTree).findText(memberName).rightClick() + } + actionMenuItem(remoteRobot, RENAME_POINT_TEXT).click() + } + } + + internal fun callEditWSFromContextMenu( + datasetName: String, + fixtureStack: MutableList, + remoteRobot: RemoteRobot + ) = with(remoteRobot) { + ideFrameImpl(PROJECT_NAME, fixtureStack) { + explorer { + fileExplorer.click() + find(viewTree).findText(datasetName).doubleClick() + find(viewTree).findText(datasetName).rightClick() + } + actionMenuItem(remoteRobot, EDIT_POINT_TEXT).click() + } + } + + internal fun callTreesElementProperty( + treesElement: String, + fixtureStack: MutableList, + remoteRobot: RemoteRobot + ) = with(remoteRobot) { + ideFrameImpl(PROJECT_NAME, fixtureStack) { + explorer { + fileExplorer.click() + find(viewTree).findText(treesElement).rightClick() + } + actionMenuItem(remoteRobot, PROPERTIES_POINT_TEXT).click() + } + } + + internal fun newMemberNameInput(newName: String, fixtureStack: MutableList, remoteRobot: RemoteRobot) = + with(remoteRobot) { + ideFrameImpl(PROJECT_NAME, fixtureStack) { + dialog(RENAME_MEMBER_NAME) { + find(inputFieldLoc).text = newName + } + } + } + + internal fun newDatasetNameInput(newName: String, fixtureStack: MutableList, remoteRobot: RemoteRobot) = + with(remoteRobot) { + ideFrameImpl(PROJECT_NAME, fixtureStack) { + dialog(RENAME_DATASET_NAME) { + find(inputFieldLoc).text = newName + } + } + } + + /** + * Checks if File Explorer contains expected message. + */ + internal fun findMessageInExplorer(msg: String, remoteRobot: RemoteRobot) = with(remoteRobot) { + if (!find(viewTree).findAllText().map(RemoteText::text).joinToString("") + .contains(msg) + ) { + throw Exception("Expected message is not found") + } + } + + internal fun closeIntelligentProject(fixtureStack: MutableList, remoteRobot: RemoteRobot) = + with(remoteRobot) { + ideFrameImpl(PROJECT_NAME, fixtureStack) { + close() + } + } + + internal fun refreshWorkSpace(wsName:String, fixtureStack: MutableList, remoteRobot: RemoteRobot) = with(remoteRobot) { + ideFrameImpl(PROJECT_NAME, fixtureStack) + { + explorer { + fileExplorer.click() + find(viewTree).findText(wsName).rightClick() + } + actionMenuItem(remoteRobot, REFRESH_POINT_TEXT).click() + } + } + + internal fun compressAndDecompressTree(wsName:String, fixtureStack: MutableList, remoteRobot: RemoteRobot) = + with(remoteRobot) { + ideFrameImpl(PROJECT_NAME, fixtureStack) { + explorer { + find(viewTree).findAllText(wsName).last().doubleClick() + } + } + } + + /** + * Closes opened dataset member or PS dataset. + */ + internal fun closeMemberOrDataset(remoteRobot: RemoteRobot) = with(remoteRobot) { + ideFrameImpl(PROJECT_NAME, fixtureStack) { + with(textEditor()) { + keyboard { + hotKey(KeyEvent.VK_CONTROL, KeyEvent.VK_F4) + } + } + } + } + + /** + * Open property + */ + internal fun openPropertyDatasetName(dsName: String, fixtureStack: MutableList, remoteRobot: RemoteRobot) = + with(remoteRobot) { + ideFrameImpl(PROJECT_NAME, fixtureStack) { + explorer { + fileExplorer.click() + Thread.sleep(1000) + find(viewTree).findText(dsName).rightClick() + } + actionMenuItem(remoteRobot, PROPERTIES_POINT_TEXT).click() + } + } + + fun openTreesElement(treesElement: String, remoteRobot: RemoteRobot) = with(remoteRobot) { + ideFrameImpl(PROJECT_NAME, fixtureStack) { + explorer { + find(viewTree).findAllText(treesElement).last().doubleClick() + } + } + } + + fun callCreateWorkingSetFromActionButton(fixtureStack: MutableList, remoteRobot: RemoteRobot) = + with(remoteRobot) { + ideFrameImpl(PROJECT_NAME, fixtureStack) { + callCreateWorkingSetFromActionButton(closableFixtureCollector, fixtureStack) + } + } + + fun callCreateWSFromContextMenu(fixtureStack: MutableList, remoteRobot: RemoteRobot) = + with(remoteRobot) { + ideFrameImpl(PROJECT_NAME, fixtureStack) { + createWSFromContextMenu(fixtureStack, closableFixtureCollector) + } + } + +// fun createWsWithConnection(connectionName: String, wsName: String, fixtureStack: MutableList, remoteRobot: RemoteRobot) = +// with(remoteRobot) { +// callCreateWSFromContextMenu(fixtureStack, remoteRobot) +// addWorkingSetDialog.fillAddWorkingSet(connectionName, wsName, fixtureStack, remoteRobot) +// okButton.click() +// } +// fun createWsWithConnection(connectionName: String, wsName: String, mask: Pair, fixtureStack: MutableList, remoteRobot: RemoteRobot) = +// with(remoteRobot) { +// callCreateWSFromContextMenu(fixtureStack, remoteRobot) +// addWorkingSetDialog.fillAddWorkingSet(connectionName, wsName, mask, fixtureStack, remoteRobot) +// okButton.click() +// } +// fun callCreateConnectionFromActionButton(fixtureStack: MutableList, remoteRobot: RemoteRobot) = +// with(remoteRobot) { +// ideFrameImpl(PROJECT_NAME, fixtureStack) { +// createConnectionFromActionButton(closableFixtureCollector, fixtureStack) +// } +// } + + fun createWsWithConnectionFromAction( + connectionName: String, + wsName: String, + fixtureStack: MutableList, + closableFixtureCollector: ClosableFixtureCollector, + remoteRobot: RemoteRobot, + ) { + callCreateWorkingSetFromActionButton(fixtureStack, remoteRobot) + closableFixtureCollector.closeOnceIfExists(AddWorkingSetDialog.name) + addWorkingSetDialog.fillAddWorkingSet(connectionName, wsName, fixtureStack, remoteRobot) + return addWorkingSetDialog.clickButtonByName(OK_TEXT) + } + + fun createWsWithConnectionFromAction(connectionName: String, wsName: String, mask: Pair, fixtureStack: MutableList, remoteRobot: RemoteRobot) { + callCreateWorkingSetFromActionButton(fixtureStack, remoteRobot) + addWorkingSetDialog.fillAddWorkingSet(connectionName, wsName, mask, fixtureStack, remoteRobot) + return addWorkingSetDialog.clickButtonByName(OK_TEXT) + } + + fun fillConnectionFilds( + connectionName: String = PROJECT_NAME, hostName: String = mockServer.hostName, port: Int = mockServer.port, + userId: String = ZOS_USERID, userPwd: String=ZOS_PWD, ssl: Boolean=true, protocol: String = "https", + fixtureStack: MutableList, remoteRobot: RemoteRobot) = with(remoteRobot) { + ideFrameImpl(PROJECT_NAME, fixtureStack) { + addConnectionDialog(fixtureStack) + { + addConnection(connectionName, "${protocol}://${hostName}:${port}", userId, userPwd, ssl) + } + } + } + + + + + fun deleteInEditWorkingSet(masks: List, fixtureStack: MutableList, remoteRobot: RemoteRobot) = with(remoteRobot) { + ideFrameImpl(PROJECT_NAME, fixtureStack) { + editWorkingSetDialog(fixtureStack) { + deleteMasks(masks) + } + } + } + + fun changeConnectionInEditWorkingSet(newConnectionName: String, fixtureStack: MutableList, remoteRobot: RemoteRobot) = with(remoteRobot) { + ideFrameImpl(PROJECT_NAME, fixtureStack) { + editWorkingSetDialog(fixtureStack) { + changeConnection(newConnectionName) + } + } + } + + + + fun fillEditWorkingSet(connectionName:String, wsName:String, mask: Pair, fixtureStack: MutableList, remoteRobot: RemoteRobot) = with(remoteRobot) { + ideFrameImpl(PROJECT_NAME, fixtureStack) { + editWorkingSetDialog(fixtureStack) { + addWorkingSet(wsName, connectionName, mask) + } + } + } + + + fun isButtonEnableByTextAddWorkingSet(buttonText:String, fixtureStack: MutableList, remoteRobot: RemoteRobot): Boolean = with(remoteRobot) { + var status = false + ideFrameImpl(PROJECT_NAME, fixtureStack) { + addWorkingSetDialog(fixtureStack) { + status = button(buttonText).isEnabled() + } + } + return status + } + + + fun hoverToByTextAddWorkingSet(text:String, fixtureStack: MutableList, remoteRobot: RemoteRobot) = with(remoteRobot) { + ideFrameImpl(PROJECT_NAME, fixtureStack) { + addWorkingSetDialog(fixtureStack) { + findText(text).moveMouse() + } + } + } + + fun clickToByTextAddWorkingSet(text:String, fixtureStack: MutableList, remoteRobot: RemoteRobot) = with(remoteRobot) { + ideFrameImpl(PROJECT_NAME, fixtureStack) { + addWorkingSetDialog(fixtureStack) { + findText(text).click() + } + } + } + + fun clickActionButtonByXpath(xpath: Locator, fixtureStack: MutableList, remoteRobot: RemoteRobot) = with(remoteRobot) { + ideFrameImpl(PROJECT_NAME, fixtureStack) { + addWorkingSetDialog(fixtureStack) { + clickActionButton(xpath) + } + } + } + + fun setInComboBox(newConnectionName: String, fixtureStack: MutableList, remoteRobot: RemoteRobot) = with(remoteRobot) { + ideFrameImpl(PROJECT_NAME, fixtureStack) { + editWorkingSetDialog(fixtureStack) { + changeConnection(newConnectionName) + } + } + } + + fun deleteWSFromContextMenu(wsName:String, fixtureStack: MutableList, remoteRobot: RemoteRobot) = with(remoteRobot) { + ideFrameImpl(PROJECT_NAME, fixtureStack) { + deleteWSFromContextMenu(wsName) + } + yesButton.click() + } + + fun callCreateMask(wsName:String, fixtureStack: MutableList, remoteRobot: RemoteRobot) = with(remoteRobot) { + ideFrameImpl(PROJECT_NAME, fixtureStack) { + createMask(wsName, fixtureStack, closableFixtureCollector) + } + } + + fun decompressWsIfCompressed(wsName1: String, compressedId: String, fixtureStack: MutableList, remoteRobot: RemoteRobot) = with(remoteRobot) { + try { + find(treesLoc).findText(compressedId) + }catch(_: NoSuchElementException){ + compressAndDecompressTree(wsName1, fixtureStack, remoteRobot) + } + } + + fun compressWsIfcDecompressed(wsName1: String, compressedId: String, fixtureStack: MutableList, remoteRobot: RemoteRobot) = with(remoteRobot) { + try { + find(treesLoc).findText(compressedId) + compressAndDecompressTree(wsName1, fixtureStack, remoteRobot) + }catch(_: NoSuchElementException){} + } +} + + + + + + + + + + + diff --git a/src/uiTest/kotlin/workingset/WorkingSetViaActionButtonTest.kt b/src/uiTest/kotlin/workingset/WorkingSetViaActionButtonTest.kt index c9618124d..6e6a6c468 100644 --- a/src/uiTest/kotlin/workingset/WorkingSetViaActionButtonTest.kt +++ b/src/uiTest/kotlin/workingset/WorkingSetViaActionButtonTest.kt @@ -16,14 +16,13 @@ import auxiliary.containers.* import com.intellij.remoterobot.RemoteRobot import com.intellij.remoterobot.fixtures.HeavyWeightWindowFixture import com.intellij.remoterobot.search.locators.Locator -import com.intellij.remoterobot.search.locators.byXpath import com.intellij.remoterobot.utils.WaitForConditionTimeoutException import io.kotest.matchers.string.shouldContain -import okhttp3.mockwebserver.MockResponse import org.junit.jupiter.api.* import org.junit.jupiter.api.Assertions.assertFalse import org.junit.jupiter.api.extension.ExtendWith -import java.time.Duration +import workingset.auxiliary.components.dialogs.AddWorkingSetSubDialog +import workingset.testutils.injectListEmptyData /** @@ -32,33 +31,35 @@ import java.time.Duration @TestMethodOrder(MethodOrderer.OrderAnnotation::class) @TestInstance(TestInstance.Lifecycle.PER_CLASS) @ExtendWith(RemoteRobotExtension::class) -class WorkingSetViaActionButtonTest { +class WorkingSetViaActionButtonTest : WorkingSetBase() { private var closableFixtureCollector = ClosableFixtureCollector() private var fixtureStack = mutableListOf() private var wantToClose = mutableListOf("Add Working Set Dialog") - - private val projectName = "untitled" - private val connectionName = "valid connection" + private val wsName1 = "WS1" + private val wsName2 = "WS2" + private val wsName3 = "WS3" + private val wsName4 = "WS4" + private val wsName5 = "WS5" /** * Opens the project and Explorer, clears test environment. */ @BeforeAll - fun setUpAll(remoteRobot: RemoteRobot) { + fun setUpAll(remoteRobot: RemoteRobot, testInfo:TestInfo) { startMockServer() - setUpTestEnvironment(projectName, fixtureStack, closableFixtureCollector, remoteRobot) + setUpTestEnvironment(fixtureStack, closableFixtureCollector, remoteRobot) + createValidConnectionWithMock(testInfo, connectionName, fixtureStack, closableFixtureCollector, remoteRobot) + addWorkingSetDialog = AddWorkingSetSubDialog(fixtureStack, remoteRobot) } /** * Closes the project and clears test environment. */ @AfterAll - fun tearDownAll(remoteRobot: RemoteRobot) = with(remoteRobot) { + fun tearDownAll(remoteRobot: RemoteRobot) { mockServer.shutdown() - clearEnvironment(projectName, fixtureStack, closableFixtureCollector, remoteRobot) - ideFrameImpl(projectName, fixtureStack) { - close() - } + clearEnvironment(fixtureStack, closableFixtureCollector, remoteRobot) + closeIntelligentProject(fixtureStack, remoteRobot) } /** @@ -73,233 +74,190 @@ class WorkingSetViaActionButtonTest { /** * Tests to add new working set without connection, checks that correct message is returned. */ - @Test - @Order(1) - fun testAddWorkingSetWithoutConnectionViaActionButton(testInfo: TestInfo, remoteRobot: RemoteRobot) = - with(remoteRobot) { - val wsName = "first ws" - responseDispatcher.injectEndpoint( - "${testInfo.displayName}_info", - { it?.requestLine?.contains("zosmf/info") ?: false }, - { MockResponse().setBody(responseDispatcher.readMockJson("infoResponse") ?: "") } - ) - responseDispatcher.injectEndpoint( - "${testInfo.displayName}_resttopology", - { it?.requestLine?.contains("zosmf/resttopology/systems") ?: false }, - { MockResponse().setBody(responseDispatcher.readMockJson("infoResponse") ?: "") } - ) - ideFrameImpl(projectName, fixtureStack) { - createWorkingSetFromActionButton(closableFixtureCollector, fixtureStack) - try { - if (dialog("Add Working Set Dialog").isShowing) { - Assertions.assertTrue(false) - } - } catch (e: WaitForConditionTimeoutException) { - e.message.shouldContain("Failed to find 'Dialog' by 'title Add Working Set Dialog'") - } finally { - closableFixtureCollector.closeOnceIfExists(AddWorkingSetDialog.name) - } - createConnectionFromActionButton(closableFixtureCollector, fixtureStack) - addConnectionDialog(fixtureStack) { - addConnection( - connectionName, - "https://${mockServer.hostName}:${mockServer.port}", - ZOS_USERID, - ZOS_PWD, - true - ) - clickButton("OK") - Thread.sleep(2000) - } - createWorkingSetFromActionButton(closableFixtureCollector, fixtureStack) - addWorkingSetDialog(fixtureStack) { - addWorkingSet(wsName, connectionName) - clickButton("OK") - Thread.sleep(2000) - find(byXpath("//div[@class='HeavyWeightWindow']")).findText( - EMPTY_DATASET_MESSAGE - ) - clickButton("OK") - Thread.sleep(2000) - } - closableFixtureCollector.closeOnceIfExists(AddWorkingSetDialog.name) - } - } + + + /** * Tests to add new empty working set with very long name, checks that correct message is returned. */ @Test - @Order(2) + @Disabled("https://jira.ibagroup.eu/browse/IJMP-977") fun testAddEmptyWorkingSetWithVeryLongNameViaActionButton(remoteRobot: RemoteRobot) = with(remoteRobot) { val wsName: String = "B".repeat(200) - ideFrameImpl(projectName, fixtureStack) { - createWorkingSetFromActionButton(closableFixtureCollector, fixtureStack) - addWorkingSetDialog(fixtureStack) { - addWorkingSet(wsName, connectionName) - clickButton("OK") - Thread.sleep(2000) - find(byXpath("//div[@class='HeavyWeightWindow']")).findText( - EMPTY_DATASET_MESSAGE - ) - clickButton("OK") - Thread.sleep(2000) - } - closableFixtureCollector.closeOnceIfExists(AddWorkingSetDialog.name) - } + callCreateWorkingSetFromActionButton(fixtureStack, remoteRobot) + addWorkingSetDialog.fillAddWorkingSet(connectionName, wsName, fixtureStack, remoteRobot) + clickByText(OK_TEXT, fixtureStack, remoteRobot) + find(messageLoc).findText(EMPTY_DATASET_MESSAGE) + clickByText(OK_TEXT, fixtureStack, remoteRobot) + closableFixtureCollector.closeOnceIfExists(AddWorkingSetDialog.name) } /** * Tests to add new working set with one valid mask. */ @Test - @Order(3) - fun testAddWorkingSetWithOneValidMaskViaActionButton(remoteRobot: RemoteRobot) = with(remoteRobot) { - val wsName = "WS1" + fun testAddWorkingSetWithOneValidMaskViaActionButton(remoteRobot: RemoteRobot){ val mask = Pair("$ZOS_USERID.*", "z/OS") - ideFrameImpl(projectName, fixtureStack) { - createWorkingSetFromActionButton(closableFixtureCollector, fixtureStack) - addWorkingSetDialog(fixtureStack) { - addWorkingSet(wsName, connectionName, mask) - clickButton("OK") - Thread.sleep(2000) - } - closableFixtureCollector.closeOnceIfExists(AddWorkingSetDialog.name) - } + callCreateWorkingSetFromActionButton(fixtureStack, remoteRobot) + addWorkingSetDialog.fillAddWorkingSet(connectionName, wsName1, mask, fixtureStack, remoteRobot) + clickByText(OK_TEXT, fixtureStack, remoteRobot) + closableFixtureCollector.closeOnceIfExists(AddWorkingSetDialog.name) } /** * Tests to add new working set with several valid z/OS masks, opens masks in explorer. */ @Test - @Order(4) - fun testAddWorkingSetWithValidZOSMasksViaActionButton(testInfo: TestInfo, remoteRobot: RemoteRobot) = - with(remoteRobot) { - val wsName = "WS2" - val masks: ArrayList> = ArrayList() - //todo allocate dataset with 44 length when 'Allocate Dataset Dialog' implemented + fun testAddWorkingSetWithValidZOSMasksViaActionButton(testInfo: TestInfo, remoteRobot: RemoteRobot){ - validZOSMasks.forEach { - masks.add(Pair(it, "z/OS")) - } - responseDispatcher.injectEndpoint( - "${testInfo.displayName}_restfiles", - { it?.requestLine?.contains("zosmf/restfiles/ds?dslevel") ?: false }, - { MockResponse().setBody("{}") } - ) + val masks: ArrayList> = ArrayList() + //todo allocate dataset with 44 length when 'Allocate Dataset Dialog' implemented - ideFrameImpl(projectName, fixtureStack) { - createWorkingSetFromActionButton(closableFixtureCollector, fixtureStack) - addWorkingSetDialog(fixtureStack) { - addWorkingSet(wsName, connectionName, masks) - clickButton("OK") - Thread.sleep(2000) - } - closableFixtureCollector.closeOnceIfExists(AddWorkingSetDialog.name) - } - validZOSMasks.forEach { - openWSOpenMaskInExplorer( - wsName, - it.uppercase(), - projectName, - fixtureStack, - remoteRobot - ) - } + validZOSMasks.forEach { + masks.add(Pair(it, ZOS_MASK)) + } + injectListEmptyData(testInfo) + callCreateWorkingSetFromActionButton(fixtureStack, remoteRobot) + addWorkingSetDialog.fillAddWorkingSet(connectionName, wsName2, masks, fixtureStack, remoteRobot) + clickByText(OK_TEXT, fixtureStack, remoteRobot) + closableFixtureCollector.closeOnceIfExists(AddWorkingSetDialog.name) + Thread.sleep(3000) + validZOSMasks.forEach { + openWSOpenMaskInExplorer( + wsName2, + it.uppercase(), + fixtureStack, + remoteRobot + ) } + } /** * Tests to add new working set with several valid USS masks, opens masks in explorer. */ @Test - @Order(5) - fun testAddWorkingSetWithValidUSSMasksViaActionButton(testInfo: TestInfo, remoteRobot: RemoteRobot) = - with(remoteRobot) { - val wsName = "WS3" - val masks: ArrayList> = ArrayList() + fun testAddWorkingSetWithValidUSSMasksViaActionButton(testInfo: TestInfo, remoteRobot: RemoteRobot){ - validUSSMasks.forEach { - masks.add(Pair(it, "USS")) - } - - responseDispatcher.injectEndpoint( - "${testInfo.displayName}_restfiles", - { it?.requestLine?.contains("zosmf/restfiles/fs?path") ?: false }, - { MockResponse().setBody("{}") } - ) + val masks: ArrayList> = ArrayList() - ideFrameImpl(projectName, fixtureStack) { - createWorkingSetFromActionButton(closableFixtureCollector, fixtureStack) - addWorkingSetDialog(fixtureStack) { - addWorkingSet(wsName, connectionName, masks) - clickButton("OK") - Thread.sleep(2000) - } - closableFixtureCollector.closeOnceIfExists(AddWorkingSetDialog.name) - } - validUSSMasks.forEach { openWSOpenMaskInExplorer(wsName, it, projectName, fixtureStack, remoteRobot) } + validUSSMasks.forEach { + masks.add(Pair(it, USS_MASK)) } + injectListEmptyData(testInfo, false) + callCreateWorkingSetFromActionButton(fixtureStack, remoteRobot) + addWorkingSetDialog.fillAddWorkingSet(connectionName, wsName3, masks, fixtureStack, remoteRobot) + clickByText(OK_TEXT, fixtureStack, remoteRobot) + closableFixtureCollector.closeOnceIfExists(AddWorkingSetDialog.name) + compressAndDecompressTree(wsName3, fixtureStack, remoteRobot) + validUSSMasks.forEach { openWSOpenMaskInExplorer(wsName3, it, fixtureStack, remoteRobot) } + } /** * Tests to add new working set with invalid masks, checks that correct messages are returned. */ @Test - @Order(6) fun testAddWorkingSetWithInvalidMasksViaActionButton(remoteRobot: RemoteRobot) = with(remoteRobot) { - val wsName = "WS4" - ideFrameImpl(projectName, fixtureStack) { - createWorkingSetFromActionButton(closableFixtureCollector, fixtureStack) - addWorkingSetDialog(fixtureStack) { - addWorkingSet(wsName, connectionName) - maskMessageMap.forEach { - addMask(Pair(it.key, "z/OS")) - if (button("OK").isEnabled()) { - clickButton("OK") - } else { - findText("OK").moveMouse() - } - if (it.key.length < 49) { - findText(it.key.uppercase()).moveMouse() - } else { - findText("${it.key.uppercase().substring(0, 46)}...").moveMouse() - } - Thread.sleep(2000) - find(byXpath("//div[@class='HeavyWeightWindow'][.//div[@class='Header']]")).findText( - it.value - ) - assertFalse(button("OK").isEnabled()) - findText(it.key.uppercase()).click() - clickActionButton(byXpath("//div[contains(@myvisibleactions, 'Down')]//div[@myaction.key='button.text.remove']")) - } - clickButton("Cancel") + + callCreateWorkingSetFromActionButton(fixtureStack, remoteRobot) + maskMessageMap.forEach { + val mask = Pair(it.key, ZOS_MASK) + addWorkingSetDialog.fillAddWorkingSet(connectionName, wsName4, mask, fixtureStack, remoteRobot) + if(isButtonEnableByTextAddWorkingSet(OK_TEXT, fixtureStack, remoteRobot)){ + clickByText(OK_TEXT, fixtureStack,remoteRobot) + } else { + hoverToByTextAddWorkingSet(OK_TEXT, fixtureStack, remoteRobot) } - closableFixtureCollector.closeOnceIfExists(AddWorkingSetDialog.name) + if (it.key.length < 49) { + hoverToByTextAddWorkingSet(it.key.uppercase(), fixtureStack, remoteRobot) + } else { + hoverToByTextAddWorkingSet("${it.key.uppercase().substring(0, 46)}...", fixtureStack, remoteRobot) + } + Thread.sleep(1000) + find(helpLoc).findText(it.value) + assertFalse(isButtonEnableByTextAddWorkingSet(OK_TEXT, fixtureStack, remoteRobot)) + clickToByTextAddWorkingSet(it.key.uppercase(), fixtureStack,remoteRobot) + Thread.sleep(3000) + clickActionButtonByXpath(removeEMaskButtonLoc, fixtureStack, remoteRobot) } + clickByText(CANCEL_TEXT, fixtureStack,remoteRobot) + closableFixtureCollector.closeOnceIfExists(AddWorkingSetDialog.name) } /** * Tests to add working set with the same masks, checks that correct message is returned. */ @Test - @Order(7) fun testAddWorkingSetWithTheSameMasksViaActionButton(remoteRobot: RemoteRobot) = with(remoteRobot) { - val wsName = "WS4" - ideFrameImpl(projectName, fixtureStack) { - createWorkingSetFromActionButton(closableFixtureCollector, fixtureStack) - addWorkingSetDialog(fixtureStack) { - addWorkingSet(wsName, connectionName) - addMask(Pair("$ZOS_USERID.*", "z/OS")) - addMask(Pair("$ZOS_USERID.*", "z/OS")) - clickButton("OK") - find( - byXpath("//div[@class='HeavyWeightWindow']"), - Duration.ofSeconds(30) - ).findText(IDENTICAL_MASKS_MESSAGE) - assertFalse(button("OK").isEnabled()) - clickButton("Cancel") + callCreateWorkingSetFromActionButton(fixtureStack, remoteRobot) + addWorkingSetDialog.fillAddWorkingSet(connectionName, wsName5, Pair("$ZOS_USERID.*", "z/OS"), fixtureStack, remoteRobot) + addWorkingSetDialog.fillAddWorkingSet(connectionName, wsName5, Pair("$ZOS_USERID.*", "z/OS"), fixtureStack, remoteRobot) + clickByText(OK_TEXT, fixtureStack,remoteRobot) + Thread.sleep(2000) + find(messageLoc).findText(IDENTICAL_MASKS_MESSAGE) + assertFalse(isButtonEnableByTextAddWorkingSet(OK_TEXT, fixtureStack, remoteRobot)) + clickByText(CANCEL_TEXT, fixtureStack,remoteRobot) + closableFixtureCollector.closeOnceIfExists(AddWorkingSetDialog.name) + } +} + +/** + * Tests creating working sets and masks via action button without creating connection + */ +@TestMethodOrder(MethodOrderer.OrderAnnotation::class) +@TestInstance(TestInstance.Lifecycle.PER_CLASS) +@ExtendWith(RemoteRobotExtension::class) +class WorkingSetViaActionButtonNoConnectionTest : WorkingSetBase(){ + private var closableFixtureCollector = ClosableFixtureCollector() + private var fixtureStack = mutableListOf() + @BeforeAll + fun setUpAll(remoteRobot: RemoteRobot, testInfo:TestInfo){ + startMockServer() + Thread.sleep(3000) + setUpTestEnvironment(fixtureStack, closableFixtureCollector, remoteRobot) + } + + @AfterAll + fun tearDownAll(remoteRobot: RemoteRobot){ + mockServer.shutdown() + clearEnvironment(fixtureStack, closableFixtureCollector, remoteRobot) + closeIntelligentProject(fixtureStack, remoteRobot) + } + @Test + @Disabled("https://jira.ibagroup.eu/browse/IJMP-977") + fun testAddWorkingSetWithoutConnectionViaActionButton(testInfo: TestInfo, remoteRobot: RemoteRobot) = + with(remoteRobot) { + val wsName = "first ws" + responseDispatcher.injectTestInfo(testInfo) + responseDispatcher.injectTestInfoRestTopology(testInfo) + callCreateWorkingSetFromActionButton(fixtureStack,remoteRobot) + + ideFrameImpl(PROJECT_NAME, fixtureStack) { + try { + if (dialog("Add Working Set Dialog").isShowing) { + Assertions.assertTrue(false) + } + } catch (e: WaitForConditionTimeoutException) { + e.message.shouldContain("Failed to find 'Dialog' by 'dialogTitle Add Working Set Dialog'") + } finally { + closableFixtureCollector.closeOnceIfExists(AddWorkingSetDialog.name) + } + + createConnectionFromActionButton(closableFixtureCollector, fixtureStack) + fillConnectionFilds(fixtureStack=fixtureStack, remoteRobot=remoteRobot) + clickByText(OK_TEXT, fixtureStack, remoteRobot) + callCreateWorkingSetFromActionButton(closableFixtureCollector, fixtureStack) + addWorkingSetDialog(fixtureStack) { + addWorkingSet(wsName, connectionName) + clickByText(OK_TEXT, fixtureStack, remoteRobot) + if (dialog("Add Working Set Dialog").isShowing) { + Assertions.assertTrue(false) + } + clickByText(OK_TEXT, fixtureStack, remoteRobot) + } + closableFixtureCollector.closeOnceIfExists(AddWorkingSetDialog.name) } - closableFixtureCollector.closeOnceIfExists(AddWorkingSetDialog.name) } - } + } diff --git a/src/uiTest/kotlin/workingset/WorkingSetViaContextMenuTest.kt b/src/uiTest/kotlin/workingset/WorkingSetViaContextMenuTest.kt index d06ceeef4..c3a8c953a 100644 --- a/src/uiTest/kotlin/workingset/WorkingSetViaContextMenuTest.kt +++ b/src/uiTest/kotlin/workingset/WorkingSetViaContextMenuTest.kt @@ -12,21 +12,19 @@ package workingset import auxiliary.* import auxiliary.closable.ClosableFixtureCollector -import auxiliary.components.actionMenuItem import auxiliary.containers.* import com.intellij.remoterobot.RemoteRobot -import com.intellij.remoterobot.fixtures.ActionButtonFixture -import com.intellij.remoterobot.fixtures.ComponentFixture import com.intellij.remoterobot.fixtures.HeavyWeightWindowFixture -import com.intellij.remoterobot.fixtures.JTextFieldFixture import com.intellij.remoterobot.search.locators.Locator import com.intellij.remoterobot.search.locators.byXpath import com.intellij.remoterobot.utils.WaitForConditionTimeoutException import io.kotest.matchers.string.shouldContain -import okhttp3.mockwebserver.MockResponse import org.junit.jupiter.api.* import org.junit.jupiter.api.Assertions.assertFalse import org.junit.jupiter.api.extension.ExtendWith +import workingset.auxiliary.components.dialogs.* +import workingset.auxiliary.components.elements.ButtonElement +import workingset.testutils.* import java.time.Duration /** @@ -35,34 +33,56 @@ import java.time.Duration @TestMethodOrder(MethodOrderer.OrderAnnotation::class) @TestInstance(TestInstance.Lifecycle.PER_CLASS) @ExtendWith(RemoteRobotExtension::class) -class WorkingSetViaContextMenuTest { +class WorkingSetViaContextMenuTest : WorkingSetBase() { private var closableFixtureCollector = ClosableFixtureCollector() private var fixtureStack = mutableListOf() - private var wantToClose = mutableListOf("Add Working Set Dialog", "Edit Working Set Dialog", "Create Mask Dialog") - - private val projectName = "untitled" - private val connectionName = "valid connection" - - - /** + private var wantToClose = mutableListOf(EDIT_WORKING_SET, CREATE_MASK_DIALOG, ADD_WORKING_SET_DIALOG) + + override val connectionName: String = "valid connection" + private val wsNameA: String = "A".repeat(200) + private val newInvalidName: String = "invalid connection" + + private val newWorkingSetName = "new ws name" + private val oldWorkingSetName = "old name" + private val alreadyExistsWorkingSetName = "already exists" + private val alreadyExistsWorkingSetNameRename = "already exists for rename" + private val wsForDelete = "ws for delete" + private val wsForInvalidMask = "ws for invalid mask" + private val wsWithUssAndDataset = "ws with uss and dataset" + private val wsWithAlreadyExistsMask = "ws with already exists mask" + private val wsWithMaskForRename = "ws with mask for rename" + private val wsWithMaskForDelete = "ws with mask for delete" + + /** * Opens the project and Explorer, clears test environment. */ @BeforeAll - fun setUpAll(remoteRobot: RemoteRobot) { + fun setUpAll(remoteRobot: RemoteRobot, testInfo:TestInfo) { + addWorkingSetDialog = AddWorkingSetSubDialog(fixtureStack, remoteRobot) startMockServer() - setUpTestEnvironment(projectName, fixtureStack, closableFixtureCollector, remoteRobot) + setUpTestEnvironment(fixtureStack, closableFixtureCollector, remoteRobot) + createValidConnectionWithMock(testInfo, connectionName, fixtureStack, closableFixtureCollector, remoteRobot) + + editWorkingSetSubDialog = EditWorkingSetSubDialog(fixtureStack=fixtureStack, remoteRobot=remoteRobot) + createMaskSubDialog = CreateMaskSubDialog(fixtureStack=fixtureStack, remoteRobot=remoteRobot) + renameDatasetMaskDialog = RenameDatasetMaskDialog(fixtureStack=fixtureStack, remoteRobot=remoteRobot) + deletionOfUssPathRoot = DeletionOfUssPathRoot(fixtureStack=fixtureStack, remoteRobot=remoteRobot) + deletionOfDSMask = DeletionOfDSMask(fixtureStack=fixtureStack, remoteRobot=remoteRobot) + + okButton = ButtonElement(OK_TEXT, fixtureStack, remoteRobot) + cancelButton = ButtonElement(CANCEL_TEXT, fixtureStack, remoteRobot) + yesButton = ButtonElement(YES_TEXT, fixtureStack, remoteRobot) + } /** * Closes the project and clears test environment. */ @AfterAll - fun tearDownAll(remoteRobot: RemoteRobot) = with(remoteRobot) { + fun tearDownAll(remoteRobot: RemoteRobot) { mockServer.shutdown() - clearEnvironment(projectName, fixtureStack, closableFixtureCollector, remoteRobot) - ideFrameImpl(projectName, fixtureStack) { - close() - } + clearEnvironment(fixtureStack, closableFixtureCollector, remoteRobot) + return closeIntelligentProject(fixtureStack, remoteRobot) } /** @@ -74,175 +94,75 @@ class WorkingSetViaContextMenuTest { closableFixtureCollector.closeWantedClosables(wantToClose, remoteRobot) } - /** - * Tests to add new working set without connection, checks that correct message is returned. - */ - @Test - @Order(1) - fun testAddWorkingSetWithoutConnectionViaContextMenu(testInfo: TestInfo, remoteRobot: RemoteRobot) = - with(remoteRobot) { - val wsName = "first ws" - responseDispatcher.injectEndpoint( - "${testInfo.displayName}_info", - { it?.requestLine?.contains("zosmf/info") ?: false }, - { MockResponse().setBody(responseDispatcher.readMockJson("infoResponse") ?: "") } - ) - responseDispatcher.injectEndpoint( - "${testInfo.displayName}_resttopology", - { it?.requestLine?.contains("zosmf/resttopology/systems") ?: false }, - { MockResponse().setBody(responseDispatcher.readMockJson("infoResponse") ?: "") } - ) - - ideFrameImpl(projectName, fixtureStack) { - createWSFromContextMenu(fixtureStack, closableFixtureCollector) - try { - if (dialog("Add Working Set Dialog").isShowing) { - Assertions.assertTrue(false) - } - } catch (e: WaitForConditionTimeoutException) { - e.message.shouldContain("Failed to find 'Dialog' by 'title Add Working Set Dialog'") - } finally { - closableFixtureCollector.closeOnceIfExists(AddWorkingSetDialog.name) - } - - createConnectionFromActionButton(closableFixtureCollector, fixtureStack) - addConnectionDialog(fixtureStack) { - addConnection( - connectionName, - "https://${mockServer.hostName}:${mockServer.port}", - ZOS_USERID, - ZOS_PWD, - true - ) - clickButton("OK") - } - closableFixtureCollector.closeOnceIfExists(AddConnectionDialog.name) - - createWSFromContextMenu(fixtureStack, closableFixtureCollector) - addWorkingSetDialog(fixtureStack) { - addWorkingSet(wsName, connectionName) - clickButton("OK") - Thread.sleep(3000) - find(byXpath("//div[@class='HeavyWeightWindow']")).findText( - EMPTY_DATASET_MESSAGE - ) - clickButton("OK") - Thread.sleep(3000) - } - closableFixtureCollector.closeOnceIfExists(AddWorkingSetDialog.name) - } - } - /** * Tests to add new empty working set with very long name, checks that correct message is returned. */ @Test - @Order(2) + @Disabled("https://jira.ibagroup.eu/browse/IJMP-977") fun testAddEmptyWorkingSetWithVeryLongNameViaContextMenu(remoteRobot: RemoteRobot) = with(remoteRobot) { - val wsName: String = "A".repeat(200) - ideFrameImpl(projectName, fixtureStack) { - createWSFromContextMenu(fixtureStack, closableFixtureCollector) - addWorkingSetDialog(fixtureStack) { - addWorkingSet(wsName, connectionName) - clickButton("OK") - Thread.sleep(3000) - find(byXpath("//div[@class='HeavyWeightWindow']")).findText( - EMPTY_DATASET_MESSAGE - ) - clickButton("OK") - Thread.sleep(3000) - } - closableFixtureCollector.closeOnceIfExists(AddWorkingSetDialog.name) - } + callCreateWSFromContextMenu(fixtureStack, remoteRobot) + addWorkingSetDialog.fillAddWorkingSet(connectionName, wsNameA, fixtureStack, remoteRobot) + clickByText(OK_TEXT, fixtureStack, remoteRobot) + find(messageLoc).findText(EMPTY_DATASET_MESSAGE) + clickByText(OK_TEXT, fixtureStack, remoteRobot) + closableFixtureCollector.closeOnceIfExists(AddWorkingSetDialog.name) + } /** * Tests to add new working set with one valid mask. */ @Test - @Order(3) - fun testAddWorkingSetWithOneValidMaskViaContextMenu(remoteRobot: RemoteRobot) = with(remoteRobot) { - val wsName = "WS1" - val mask = Pair("$ZOS_USERID.*", "z/OS") - ideFrameImpl(projectName, fixtureStack) { - createWSFromContextMenu(fixtureStack, closableFixtureCollector) - addWorkingSetDialog(fixtureStack) { - addWorkingSet(wsName, connectionName, mask) - clickButton("OK") - Thread.sleep(3000) - } - closableFixtureCollector.closeOnceIfExists(AddWorkingSetDialog.name) - } + fun testAddWorkingSetWithOneValidMaskViaContextMenu(remoteRobot: RemoteRobot) { + callCreateWSFromContextMenu(fixtureStack, remoteRobot) + addWorkingSetDialog.fillAddWorkingSet(connectionName, WS_NAME_WS_1, singleMask, fixtureStack, remoteRobot) + clickByText(OK_TEXT, fixtureStack, remoteRobot) + closableFixtureCollector.closeOnceIfExists(AddWorkingSetDialog.name) + return deleteWSFromContextMenu(WS_NAME_WS_1, fixtureStack, remoteRobot) } /** * Tests to add new working set with several valid z/OS masks, opens masks in explorer. */ @Test - @Order(4) - fun testAddWorkingSetWithValidZOSMasksViaContextMenu(testInfo: TestInfo, remoteRobot: RemoteRobot) = - with(remoteRobot) { - val wsName = "WS2" - val masks: ArrayList> = ArrayList() - //todo allocate dataset with 44 length when 'Allocate Dataset Dialog' implemented - - validZOSMasks.forEach { - masks.add(Pair(it, "z/OS")) - } - responseDispatcher.injectEndpoint( - "${testInfo.displayName}_restfiles", - { it?.requestLine?.contains("zosmf/restfiles/ds?dslevel=") ?: false }, - { MockResponse().setBody("{}") } + fun testAddWorkingSetWithValidZOSMasksViaContextMenu(testInfo: TestInfo, remoteRobot: RemoteRobot) { + //todo allocate dataset with 44 length when 'Allocate Dataset Dialog' implemented + val masks: ArrayList> = ArrayList() + validZOSMasks.forEach {masks.add(Pair(it, ZOS_MASK))} + injectListEmptyData(testInfo) + callCreateWSFromContextMenu(fixtureStack, remoteRobot) + addWorkingSetDialog.fillAddWorkingSet(connectionName, WS_NAME_WS_2, masks, fixtureStack, remoteRobot) + clickByText(OK_TEXT, fixtureStack, remoteRobot) + closableFixtureCollector.closeOnceIfExists(AddWorkingSetDialog.name) + compressWsIfcDecompressed(WS_NAME_WS_2, validZOSMasks[0],fixtureStack,remoteRobot) + validZOSMasks.forEach { + openWSOpenMaskInExplorer( + WS_NAME_WS_2, + it.uppercase(), + fixtureStack, + remoteRobot ) - - ideFrameImpl(projectName, fixtureStack) { - createWSFromContextMenu(fixtureStack, closableFixtureCollector) - addWorkingSetDialog(fixtureStack) { - addWorkingSet(wsName, connectionName, masks) - clickButton("OK") - Thread.sleep(3000) - } - closableFixtureCollector.closeOnceIfExists(AddWorkingSetDialog.name) - } - validZOSMasks.forEach { - openWSOpenMaskInExplorer( - wsName, - it.uppercase(), - projectName, - fixtureStack, - remoteRobot - ) - } } + return deleteWSFromContextMenu(WS_NAME_WS_2, fixtureStack, remoteRobot) + } /** * Tests to add new working set with several valid USS masks, opens masks in explorer. */ @Test - @Order(5) - fun testAddWorkingSetWithValidUSSMasksViaContextMenu(testInfo: TestInfo, remoteRobot: RemoteRobot) = - with(remoteRobot) { - val wsName = "WS3" - val masks: ArrayList> = ArrayList() - validUSSMasks.forEach { - masks.add(Pair(it, "USS")) - } - responseDispatcher.injectEndpoint( - "${testInfo.displayName}_restfiles", - { it?.requestLine?.contains("zosmf/restfiles/fs?path") ?: false }, - { MockResponse().setBody("{}") } - ) - - ideFrameImpl(projectName, fixtureStack) { - createWSFromContextMenu(fixtureStack, closableFixtureCollector) - addWorkingSetDialog(fixtureStack) { - addWorkingSet(wsName, connectionName, masks) - clickButton("OK") - Thread.sleep(3000) - } - closableFixtureCollector.closeOnceIfExists(AddWorkingSetDialog.name) - } - validUSSMasks.forEach { openWSOpenMaskInExplorer(wsName, it, projectName, fixtureStack, remoteRobot) } + fun testAddWorkingSetWithValidUSSMasksViaContextMenu(testInfo: TestInfo, remoteRobot: RemoteRobot){ + val masks: ArrayList> = ArrayList() + validUSSMasks.forEach { + masks.add(Pair(it, USS_MASK)) + } + injectListEmptyData(testInfo, false) + callCreateWSFromContextMenu(fixtureStack, remoteRobot) + addWorkingSetDialog.fillAddWorkingSet(connectionName, WS_NAME_WS_3, masks, fixtureStack, remoteRobot) + addWorkingSetDialog.okButton.click() + closableFixtureCollector.closeOnceIfExists(AddWorkingSetDialog.name) + compressAndDecompressTree(WS_NAME_WS_3, fixtureStack, remoteRobot) + validUSSMasks.forEach { openWSOpenMaskInExplorer(WS_NAME_WS_3, it, fixtureStack, remoteRobot) } + deleteWSFromContextMenu(WS_NAME_WS_3, fixtureStack, remoteRobot) } @@ -250,504 +170,455 @@ class WorkingSetViaContextMenuTest { * Tests to add new working set with invalid masks, checks that correct messages are returned. */ @Test - @Order(6) fun testAddWorkingSetWithInvalidMasksViaContextMenu(remoteRobot: RemoteRobot) = with(remoteRobot) { - val wsName = "WS4" - ideFrameImpl(projectName, fixtureStack) { - createWSFromContextMenu(fixtureStack, closableFixtureCollector) - addWorkingSetDialog(fixtureStack) { - addWorkingSet(wsName, connectionName) - maskMessageMap.forEach { - addMask(Pair(it.key, "z/OS")) - if (button("OK").isEnabled()) { - clickButton("OK") - } else { - findText("OK").moveMouse() - } - if (it.key.length < 49) { - findText(it.key.uppercase()).moveMouse() - } else { - findText("${it.key.uppercase().substring(0, 46)}...").moveMouse() - } - Thread.sleep(3000) - find(byXpath("//div[@class='HeavyWeightWindow'][.//div[@class='Header']]")).findText( - it.value - ) - assertFalse(button("OK").isEnabled()) - findText(it.key.uppercase()).click() - clickActionButton(byXpath("//div[contains(@myvisibleactions, 'Down')]//div[@myaction.key='button.text.remove']")) - } - clickButton("Cancel") + callCreateWSFromContextMenu(fixtureStack, remoteRobot) + maskMessageMap.forEach { + val mask = Pair(it.key, ZOS_MASK) + addWorkingSetDialog.fillAddWorkingSet(connectionName, WS_NAME_WS_4, mask, fixtureStack, remoteRobot) + if(addWorkingSetDialog.okButton.isEnabled()){ + addWorkingSetDialog.okButton.click() + } else { + hoverToByTextAddWorkingSet(OK_TEXT, fixtureStack, remoteRobot) } - closableFixtureCollector.closeOnceIfExists(AddWorkingSetDialog.name) + if (it.key.length < 49) { + hoverToByTextAddWorkingSet(it.key.uppercase(), fixtureStack, remoteRobot) + } else { + hoverToByTextAddWorkingSet("${it.key.uppercase().substring(0, 46)}...", fixtureStack, remoteRobot) + } + Thread.sleep(1000) + find(helpLoc).findText(it.value) + assertFalse(addWorkingSetDialog.okButton.isEnabled()) + clickToByTextAddWorkingSet(it.key.uppercase(), fixtureStack,remoteRobot) + Thread.sleep(3000) + clickActionButtonByXpath(removeEMaskButtonLoc, fixtureStack, remoteRobot) } + cancelButton.click() + closableFixtureCollector.closeOnceIfExists(AddWorkingSetDialog.name) } /** * Tests to add working set with the same masks, checks that correct message is returned. */ @Test - @Order(7) fun testAddWorkingSetWithTheSameMasksViaContextMenu(remoteRobot: RemoteRobot) = with(remoteRobot) { - val wsName = "WS4" - ideFrameImpl(projectName, fixtureStack) { - createWSFromContextMenu(fixtureStack, closableFixtureCollector) - addWorkingSetDialog(fixtureStack) { - addWorkingSet(wsName, connectionName) - addMask(Pair("$ZOS_USERID.*", "z/OS")) - addMask(Pair("$ZOS_USERID.*", "z/OS")) - clickButton("OK") - find( - byXpath("//div[@class='HeavyWeightWindow']"), - Duration.ofSeconds(30) - ).findText(IDENTICAL_MASKS_MESSAGE) - assertFalse(button("OK").isEnabled()) - clickButton("Cancel") - } - closableFixtureCollector.closeOnceIfExists(AddWorkingSetDialog.name) - } + callCreateWSFromContextMenu(fixtureStack, remoteRobot) + addWorkingSetDialog.fillAddWorkingSet(connectionName, WS_NAME_WS_5, singleMask, fixtureStack, remoteRobot) + addWorkingSetDialog.fillAddWorkingSet(connectionName, WS_NAME_WS_5, singleMask, fixtureStack, remoteRobot) + addWorkingSetDialog.okButton.click() + Thread.sleep(2000) + find(messageLoc).findText(IDENTICAL_MASKS_MESSAGE) + assertFalse(isButtonEnableByTextAddWorkingSet(OK_TEXT, fixtureStack, remoteRobot)) + addWorkingSetDialog.cancelButton.click() + closableFixtureCollector.closeOnceIfExists(AddWorkingSetDialog.name) } /** * Tests to edit working set by adding one mask, checks that ws is refreshed in explorer, opens new mask. */ @Test - @Order(8) - fun testEditWorkingSetAddOneMaskViaContextMenu(testInfo: TestInfo, remoteRobot: RemoteRobot) = with(remoteRobot) { - val wsName = "WS1" - responseDispatcher.injectEndpoint( - "${testInfo.displayName}_restfiles", - { it?.requestLine?.contains("zosmf/restfiles/") ?: false }, - { MockResponse().setBody("{}") } - ) - openOrCloseWorkingSetInExplorer(wsName, projectName, fixtureStack, remoteRobot) - closeMaskInExplorer("$ZOS_USERID.*".uppercase(), projectName, fixtureStack, remoteRobot) - ideFrameImpl(projectName, fixtureStack) { - editWSFromContextMenu(wsName, fixtureStack, closableFixtureCollector) - editWorkingSetDialog(fixtureStack) { - addMask(Pair("/u/$ZOS_USERID", "USS")) - clickButton("OK") - Thread.sleep(3000) - } - closableFixtureCollector.closeOnceIfExists(EditWorkingSetDialog.name) - } - openMaskInExplorer("/u/$ZOS_USERID", "", projectName, fixtureStack, remoteRobot) - openOrCloseWorkingSetInExplorer(wsName, projectName, fixtureStack, remoteRobot) + fun testEditWorkingSetAddOneMaskViaContextMenu(testInfo: TestInfo, remoteRobot: RemoteRobot) { + injectListEmptyData(testInfo) + callCreateWSFromContextMenu(fixtureStack, remoteRobot) + addWorkingSetDialog.fillAddWorkingSet(connectionName, WS_NAME_WS_6, singleMask, fixtureStack, remoteRobot) + okButton.click() + callEditWSFromContextMenu(WS_NAME_WS_6, fixtureStack, remoteRobot) + fillEditWorkingSet(connectionName,WS_NAME_WS_6,singleUssMask,fixtureStack, remoteRobot) + okButton.click() + closableFixtureCollector.closeOnceIfExists(EditWorkingSetDialog.name) + decompressWsIfCompressed(WS_NAME_WS_6, ussMask, fixtureStack, remoteRobot) + openMaskInExplorer(ussMask,"", fixtureStack, remoteRobot) + openOrCloseWorkingSetInExplorer(WS_NAME_WS_6, fixtureStack, remoteRobot) + return deleteWSFromContextMenu(WS_NAME_WS_6, fixtureStack, remoteRobot) } /** * Tests to edit working set by deleting several masks, checks that ws is refreshed in explorer and masks were deleted. */ @Test - @Order(9) - fun testEditWorkingSetDeleteMasksViaContextMenu(remoteRobot: RemoteRobot) = with(remoteRobot) { - val wsName = "WS2" - val masks = listOf("$ZOS_USERID.*".uppercase(), "Q.*", ZOS_USERID.uppercase()) - openOrCloseWorkingSetInExplorer(wsName, projectName, fixtureStack, remoteRobot) - ideFrameImpl(projectName, fixtureStack) { - editWSFromContextMenu(wsName, fixtureStack, closableFixtureCollector) - editWorkingSetDialog(fixtureStack) { - deleteMasks(masks) - clickButton("OK") - Thread.sleep(5000) - } - closableFixtureCollector.closeOnceIfExists(EditWorkingSetDialog.name) - } - masks.forEach { checkItemWasDeletedWSRefreshed(it.uppercase(), projectName, fixtureStack, remoteRobot) } - openOrCloseWorkingSetInExplorer(wsName, projectName, fixtureStack, remoteRobot) + fun testEditWorkingSetDeleteMasksViaContextMenu(remoteRobot: RemoteRobot) { + val masks = listOf(zosUserDatasetMask, "Q.*", ZOS_USERID.uppercase()) + callCreateWSFromContextMenu(fixtureStack, remoteRobot) + masks.forEach {addWorkingSetDialog.fillAddWorkingSet(connectionName, WS_NAME_WS_7, Pair(it, ZOS_MASK), fixtureStack, remoteRobot)} + okButton.click() + callEditWSFromContextMenu(WS_NAME_WS_7, fixtureStack, remoteRobot) + deleteInEditWorkingSet(masks, fixtureStack, remoteRobot) + okButton.click() + + masks.forEach { checkItemWasDeletedWSRefreshed(it.uppercase(), fixtureStack, remoteRobot) } + openOrCloseWorkingSetInExplorer(WS_NAME_WS_7, fixtureStack, remoteRobot) + deleteWSFromContextMenu(WS_NAME_WS_7, fixtureStack, remoteRobot) + return closableFixtureCollector.closeOnceIfExists(EditWorkingSetDialog.name) } /** * Tests to edit working set by deleting all masks, checks that ws is refreshed in explorer and masks were deleted. */ @Test - @Order(10) + @Disabled("https://jira.ibagroup.eu/browse/IJMP-977") fun testEditWorkingSetDeleteAllMasksViaContextMenu(remoteRobot: RemoteRobot) = with(remoteRobot) { - val wsName = "WS2" - val deletedMasks = listOf("$ZOS_USERID.**", "$ZOS_USERID.@#%", "$ZOS_USERID.@#%.*", "WWW.*", maskWithLength44) - openOrCloseWorkingSetInExplorer(wsName, projectName, fixtureStack, remoteRobot) - ideFrameImpl(projectName, fixtureStack) { - editWSFromContextMenu(wsName, fixtureStack, closableFixtureCollector) + callCreateWSFromContextMenu(fixtureStack, remoteRobot) + validZOSMasks.forEach {addWorkingSetDialog.fillAddWorkingSet(connectionName, WS_NAME_WS_8, Pair(it, ZOS_MASK), fixtureStack, remoteRobot)} + addWorkingSetDialog.okButton.click() + openOrCloseWorkingSetInExplorer(WS_NAME_WS_8, fixtureStack, remoteRobot) + callEditWSFromContextMenu(WS_NAME_WS_8, fixtureStack, remoteRobot) + deleteInEditWorkingSet(validZOSMasks, fixtureStack, remoteRobot) + okButton.click() + ideFrameImpl(PROJECT_NAME, fixtureStack) { editWorkingSetDialog(fixtureStack) { - deleteAllMasks() - clickButton("OK") - find(byXpath("//div[@class='HeavyWeightWindow']")).findText( - EMPTY_DATASET_MESSAGE - ) - clickButton("OK") + find(messageLoc).findText(EMPTY_DATASET_MESSAGE) + okButton.click() Thread.sleep(5000) } closableFixtureCollector.closeOnceIfExists(EditWorkingSetDialog.name) } - deletedMasks.forEach { checkItemWasDeletedWSRefreshed(it.uppercase(), projectName, fixtureStack, remoteRobot) } - openOrCloseWorkingSetInExplorer(wsName, projectName, fixtureStack, remoteRobot) + validZOSMasks.forEach { checkItemWasDeletedWSRefreshed(it.uppercase(), fixtureStack, remoteRobot) } + openOrCloseWorkingSetInExplorer(WS_NAME_WS_8, fixtureStack, remoteRobot) } /** * Tests to edit working set by changing connection to invalid, checks that correct message is returned. */ @Test - @Order(11) - fun testEditWorkingSetChangeConnectionToInvalidViaContextMenu(testInfo: TestInfo, remoteRobot: RemoteRobot) = - with(remoteRobot) { - val newConnectionName = "invalid connection" - val testPort = "10443" - responseDispatcher.injectEndpoint( - "${testInfo.displayName}_info", - { it?.requestLine?.contains("zosmf/info") ?: false }, - { MockResponse().setBody("Invalid URL port: \"${testPort}1\"") } - ) - responseDispatcher.injectEndpoint( - "${testInfo.displayName}_second", - { it?.requestLine?.contains("zosmf/restfiles/") ?: false }, - { MockResponse().setBody("{}") } - ) - createConnection( - projectName, - fixtureStack, - closableFixtureCollector, - newConnectionName, - false, - remoteRobot, - "https://${mockServer.hostName}:$testPort" - ) - val wsName = "WS1" - openOrCloseWorkingSetInExplorer(wsName, projectName, fixtureStack, remoteRobot) - ideFrameImpl(projectName, fixtureStack) { - editWSFromContextMenu(wsName, fixtureStack, closableFixtureCollector) - editWorkingSetDialog(fixtureStack) { - changeConnection(newConnectionName) - clickButton("OK") - Thread.sleep(5000) - } - closableFixtureCollector.closeOnceIfExists(EditWorkingSetDialog.name) - } - findAll(byXpath("//div[@class='MyComponent'][.//div[@accessiblename='Invalid URL port: \"104431\"' and @class='JEditorPane']]")).forEach { - it.click() - findAll( - byXpath("//div[@class='ActionButton' and @myicon= 'close.svg']") - ).first().click() - } - openMaskInExplorer( - "$ZOS_USERID.*".uppercase(), - "Invalid URL port: \"104431\"", - projectName, - fixtureStack, - remoteRobot - ) - } + fun testEditWorkingSetChangeConnectionToInvalidViaContextMenu(testInfo: TestInfo, remoteRobot: RemoteRobot) { + injectInvalidUrlPortInfo(testInfo,PORT_104431_AND_1) + injectEmptyZosmfRestfilesPath(testInfo) + createConnection(fixtureStack, closableFixtureCollector, newInvalidName, false, remoteRobot, "https://${mockServer.hostName}:$PORT_104431") + + callCreateWSFromContextMenu(fixtureStack, remoteRobot) + validZOSMasks.forEach {addWorkingSetDialog.fillAddWorkingSet(connectionName, WS_NAME_WS_9, Pair(it, ZOS_MASK), fixtureStack, remoteRobot)} + addWorkingSetDialog.okButton.click() + + openOrCloseWorkingSetInExplorer(WS_NAME_WS_9, fixtureStack, remoteRobot) + callEditWSFromContextMenu(WS_NAME_WS_9, fixtureStack, remoteRobot) + setInComboBox(newInvalidName,fixtureStack,remoteRobot) + okButton.click() + closableFixtureCollector.closeOnceIfExists(EditWorkingSetDialog.name) + + openMaskInExplorer( + zosUserDatasetMask, INVALID_URL_PORT.format(PORT_104431_AND_1), fixtureStack, remoteRobot + ) + return deleteWSFromContextMenu(WS_NAME_WS_9, fixtureStack, remoteRobot) + } /** * Tests to edit working set by changing connection from invalid to valid, checks that ws is refreshed in explorer and error message disappeared. */ @Test - @Order(12) - fun testEditWorkingSetChangeConnectionToValidViaContextMenu(testInfo: TestInfo, remoteRobot: RemoteRobot) = - with(remoteRobot) { - val newConnectionName = "new $connectionName" - responseDispatcher.injectEndpoint( - "${testInfo.displayName}_info", - { it?.requestLine?.contains("zosmf/info") ?: false }, - { MockResponse().setBody(responseDispatcher.readMockJson("infoResponse") ?: "") } - ) - responseDispatcher.injectEndpoint( - "${testInfo.displayName}_resttopology", - { it?.requestLine?.contains("zosmf/resttopology/systems") ?: false }, - { MockResponse().setBody(responseDispatcher.readMockJson("infoResponse") ?: "") } - ) - createConnection( - projectName, - fixtureStack, - closableFixtureCollector, - newConnectionName, - true, - remoteRobot, - "https://${mockServer.hostName}:${mockServer.port}" - ) - val wsName = "WS1" - responseDispatcher.injectEndpoint( - "${testInfo.displayName}_restfiles", - { it?.requestLine?.contains("zosmf/restfiles") ?: false }, - { MockResponse().setBody("{}") } - ) - ideFrameImpl(projectName, fixtureStack) { - editWSFromContextMenu(wsName, fixtureStack, closableFixtureCollector) - editWorkingSetDialog(fixtureStack) { - changeConnection(newConnectionName) - clickButton("OK") - Thread.sleep(5000) - } - closableFixtureCollector.closeOnceIfExists(EditWorkingSetDialog.name) - } - checkItemWasDeletedWSRefreshed("Invalid URL port: \"104431\"", projectName, fixtureStack, remoteRobot) - openOrCloseWorkingSetInExplorer(wsName, projectName, fixtureStack, remoteRobot) - } + fun testEditWorkingSetChangeConnectionToValidViaContextMenu(testInfo: TestInfo, remoteRobot: RemoteRobot) { + injectInvalidUrlPortInfo(testInfo,PORT_104431_AND_1) + injectTestInfoRestTopology(testInfo) + createConnection(fixtureStack, closableFixtureCollector, WS_NAME_WS_10, false, remoteRobot, "https://${mockServer.hostName}:$PORT_104431") + + callCreateWSFromContextMenu(fixtureStack, remoteRobot) + validZOSMasks.forEach {addWorkingSetDialog.fillAddWorkingSet(WS_NAME_WS_10, WS_NAME_WS_10, Pair(it, ZOS_MASK), fixtureStack, remoteRobot)} + addWorkingSetDialog.okButton.click() + + injectEmptyZosmfRestfilesPath(testInfo) + callEditWSFromContextMenu(WS_NAME_WS_10, fixtureStack, remoteRobot) + changeConnectionInEditWorkingSet(connectionName, fixtureStack, remoteRobot) + okButton.click() + + openOrCloseWorkingSetInExplorer(WS_NAME_WS_10, fixtureStack, remoteRobot) + checkItemWasDeletedWSRefreshed(INVALID_URL_PORT.format(PORT_104431_AND_1), fixtureStack, remoteRobot) + closableFixtureCollector.closeOnceIfExists(EditWorkingSetDialog.name) + return deleteWSFromContextMenu(WS_NAME_WS_10, fixtureStack, remoteRobot) + } /** * Tests to edit working set by renaming it, checks that ws is refreshed in explorer. */ @Test - @Order(13) fun testEditWorkingSetRenameViaContextMenu(remoteRobot: RemoteRobot) = with(remoteRobot) { - val newWorkingSetName = "new ws name" - val oldWorkingSetName = "WS1" - val alreadyExistsWorkingSetName = "WS2" - openOrCloseWorkingSetInExplorer(oldWorkingSetName, projectName, fixtureStack, remoteRobot) - ideFrameImpl(projectName, fixtureStack) { - editWSFromContextMenu(oldWorkingSetName, fixtureStack, closableFixtureCollector) - editWorkingSetDialog(fixtureStack) { - renameWorkingSet(alreadyExistsWorkingSetName) - clickButton("OK") - val message = find( - byXpath("//div[@class='HeavyWeightWindow']"), - Duration.ofSeconds(30) - ).findAllText() - (message[0].text + message[1].text).shouldContain("You must provide unique working set name. Working Set $alreadyExistsWorkingSetName already exists.") - renameWorkingSet(newWorkingSetName) - clickButton("OK") - Thread.sleep(5000) - } - closableFixtureCollector.closeOnceIfExists(EditWorkingSetDialog.name) - } - checkItemWasDeletedWSRefreshed(oldWorkingSetName, projectName, fixtureStack, remoteRobot) - openOrCloseWorkingSetInExplorer(newWorkingSetName, projectName, fixtureStack, remoteRobot) + createWsWithConnectionFromAction(connectionName, oldWorkingSetName, singleMask, fixtureStack, remoteRobot) + + createWsWithConnectionFromAction( + connectionName, alreadyExistsWorkingSetName, singleMask, fixtureStack, remoteRobot) + + callEditWSFromContextMenu(oldWorkingSetName, fixtureStack, remoteRobot) + + editWorkingSetSubDialog.renameWorkingSet(alreadyExistsWorkingSetName) + clickByText(OK_TEXT, fixtureStack, remoteRobot) + + val message = find(messageLoc,Duration.ofSeconds(30)).findAllText() + (message[0].text + message[1].text).shouldContain(UNIQUE_WORKING_SET_NAME.format(alreadyExistsWorkingSetName)) + + editWorkingSetSubDialog.renameWorkingSet(newWorkingSetName) + clickByText(OK_TEXT, fixtureStack, remoteRobot) + + closableFixtureCollector.closeOnceIfExists(EditWorkingSetDialog.name) + + checkItemWasDeletedWSRefreshed(oldWorkingSetName, fixtureStack, remoteRobot) + openOrCloseWorkingSetInExplorer(newWorkingSetName, fixtureStack, remoteRobot) + deleteWSFromContextMenu(alreadyExistsWorkingSetName, fixtureStack, remoteRobot) + deleteWSFromContextMenu(newWorkingSetName, fixtureStack, remoteRobot) } /** * Tests to delete working set, checks that explorer info is refreshed. */ @Test - @Order(14) - fun testDeleteWorkingSetViaContextMenu(remoteRobot: RemoteRobot) = with(remoteRobot) { - val wsName = "WS2" - ideFrameImpl(projectName, fixtureStack) { - deleteWSFromContextMenu(wsName) - clickButton("Yes") - } - checkItemWasDeletedWSRefreshed(wsName, projectName, fixtureStack, remoteRobot) + fun testDeleteWorkingSetViaContextMenu(remoteRobot: RemoteRobot) { + createWsWithConnectionFromAction(connectionName, wsForDelete, fixtureStack, closableFixtureCollector, remoteRobot) + + deleteWSFromContextMenu(wsForDelete, fixtureStack,remoteRobot) + + return checkItemWasDeletedWSRefreshed(wsForDelete, fixtureStack, remoteRobot) } /** * Tests to create invalid masks, checks that correct messages are returned. */ @Test - @Order(15) fun testCreateInvalidMasksViaContextMenu(remoteRobot: RemoteRobot) = with(remoteRobot) { - val wsName = "first ws" - ideFrameImpl(projectName, fixtureStack) { - createMask(wsName, fixtureStack, closableFixtureCollector) - createMaskDialog(fixtureStack) { - maskMessageMap.forEach { - createMask(Pair(it.key, "z/OS")) - Thread.sleep(3000) - if (button("OK").isEnabled()) { - clickButton("OK") - } - find(byXpath("//div[@class='HeavyWeightWindow']")).findText( - it.value - ) - assertFalse(button("OK").isEnabled()) - } - clickButton("Cancel") + createWsWithConnectionFromAction(connectionName, wsForInvalidMask, fixtureStack, closableFixtureCollector, remoteRobot) + + callCreateMask(wsForInvalidMask, fixtureStack,remoteRobot) + maskMessageMap.forEach { + createMaskSubDialog.setMask(Pair(it.key, ZOS_MASK)) + Thread.sleep(3000) + if (okButton.isEnabled()) { + okButton.click() } - closableFixtureCollector.closeOnceIfExists(CreateMaskDialog.name) + find(messageLoc).findText( + it.value + ) + assertFalse(okButton.isEnabled()) } + cancelButton.click() + closableFixtureCollector.closeOnceIfExists(createMaskSubDialog.dialogTitle) + deleteWSFromContextMenu(wsForInvalidMask, fixtureStack, remoteRobot) } /** * Tests to create valid USS and z/OS masks from context menu. */ @Test - @Order(16) - fun testCreateValidMasksViaContextMenu(testInfo: TestInfo, remoteRobot: RemoteRobot) = with(remoteRobot) { - val wsName = "first ws" - responseDispatcher.injectEndpoint( - "${testInfo.displayName}_restfiles", - { it?.requestLine?.contains("zosmf/restfiles/") ?: false }, - { MockResponse().setBody("{}") } - ) + fun testCreateValidMasksViaContextMenu(testInfo: TestInfo, remoteRobot: RemoteRobot) { + createWsWithConnectionFromAction(connectionName, wsWithUssAndDataset, fixtureStack, closableFixtureCollector, remoteRobot) + + injectEmptyZosmfRestfilesPath(testInfo) + validZOSMasks.forEach { - createMaskFromContextMenu(wsName, it, "z/OS", remoteRobot) - openWSOpenMaskInExplorer(wsName, it.uppercase(), projectName, fixtureStack, remoteRobot) + createMask(wsWithUssAndDataset, it, fixtureStack,closableFixtureCollector, ZOS_MASK, remoteRobot) + openWSOpenMaskInExplorer(wsWithUssAndDataset, it.uppercase(), fixtureStack, remoteRobot) + closableFixtureCollector.closeOnceIfExists(CreateMaskDialog.name) } validUSSMasks.forEach { - createMaskFromContextMenu(wsName, it, "USS", remoteRobot) + createMask(wsWithUssAndDataset, it, fixtureStack,closableFixtureCollector, USS_MASK, remoteRobot) + closableFixtureCollector.closeOnceIfExists(CreateMaskDialog.name) } + return deleteWSFromContextMenu(wsWithUssAndDataset, fixtureStack, remoteRobot) } /** * Tests to create already exists mask in working set, checks that correct message is returned. */ @Test - @Order(17) fun testCreateAlreadyExistsMaskViaContextMenu(remoteRobot: RemoteRobot) = with(remoteRobot) { - val wsName = "first ws" - val maskName = "$ZOS_USERID.*" - ideFrameImpl(projectName, fixtureStack) { - createMask(wsName, fixtureStack, closableFixtureCollector) - createMaskDialog(fixtureStack) { - createMask(Pair(maskName, "z/OS")) - clickButton("OK") - assertFalse(button("OK").isEnabled()) - val message = find( - byXpath("//div[@class='HeavyWeightWindow']"), - Duration.ofSeconds(30) - ).findAllText() - (message[0].text + message[1].text).shouldContain("You must provide unique mask in working set. Working Set \"$wsName\" already has mask - $maskName") - clickButton("Cancel") - } - closableFixtureCollector.closeOnceIfExists(CreateMaskDialog.name) - } + createWsWithConnectionFromAction(connectionName, wsWithAlreadyExistsMask, fixtureStack, closableFixtureCollector, remoteRobot) + createMask(wsWithAlreadyExistsMask, zosUserDatasetMask, fixtureStack,closableFixtureCollector, ZOS_MASK, remoteRobot) + + callCreateMask(wsWithAlreadyExistsMask, fixtureStack,remoteRobot) + createMaskSubDialog.setMask(singleMask) + okButton.click() + assertFalse(okButton.isEnabled()) + + val message = find(messageLoc,Duration.ofSeconds(30)).findAllText() + (message[0].text + message[1].text).shouldContain(UNIQUE_MASK.format(wsWithAlreadyExistsMask, zosUserDatasetMask)) + cancelButton.click() + closableFixtureCollector.closeOnceIfExists(CreateMaskDialog.name) + deleteWSFromContextMenu(wsWithAlreadyExistsMask, fixtureStack, remoteRobot) + } /** * Tests to rename mask, checks that info is refreshed in explorer. */ @Test - @Order(18) - fun testRenameMasksViaContextMenu(testInfo: TestInfo, remoteRobot: RemoteRobot) = with(remoteRobot) { - val wsName = "new ws name" - responseDispatcher.injectEndpoint( - "${testInfo.displayName}_restfiles", - { it?.requestLine?.contains("zosmf/restfiles/") ?: false }, - { MockResponse().setBody("{}") } - ) - renameMaskFromContextMenu( - wsName, - "$ZOS_USERID.*".uppercase(), - "$ZOS_USERID.**", - true, - "Rename Dataset Mask", - remoteRobot - ) - renameMaskFromContextMenu(wsName, "/u/$ZOS_USERID", "/etc/ssh", true, "Rename USS Mask", remoteRobot) - openWSOpenMaskInExplorer(wsName, "$ZOS_USERID.**".uppercase(), projectName, fixtureStack, remoteRobot) - openWSOpenMaskInExplorer(wsName, "/etc/ssh", projectName, fixtureStack, remoteRobot) - openOrCloseWorkingSetInExplorer(wsName, projectName, fixtureStack, remoteRobot) - checkItemWasDeletedWSRefreshed("$ZOS_USERID.*".uppercase(), projectName, fixtureStack, remoteRobot) - checkItemWasDeletedWSRefreshed("/u/$ZOS_USERID", projectName, fixtureStack, remoteRobot) - openOrCloseWorkingSetInExplorer(wsName, projectName, fixtureStack, remoteRobot) + fun testRenameMasksViaContextMenu(testInfo: TestInfo, remoteRobot: RemoteRobot) { + createWsWithConnectionFromAction(connectionName, wsWithMaskForRename, fixtureStack, closableFixtureCollector, remoteRobot) + + createMask(wsWithMaskForRename, zosUserDatasetMask, fixtureStack,closableFixtureCollector, ZOS_MASK, remoteRobot) + createMask(wsWithMaskForRename, ussMask, fixtureStack,closableFixtureCollector, USS_MASK, remoteRobot) + + injectEmptyZosmfRestfilesPath(testInfo) + compressAndDecompressTree(wsWithMaskForRename, fixtureStack, remoteRobot) + + callEditWSFromContextMenu(zosUserDatasetMask, fixtureStack, remoteRobot) + renameDatasetMaskDialog.renameMaskFromContextMenu(zosUserDatasetMaskDoubleStar, remoteRobot) + okButton.click() + + callEditWSFromContextMenu(ussMask, fixtureStack, remoteRobot) + renameDatasetMaskDialog.renameMaskFromContextMenu(defaultNewUssMask, remoteRobot) + okButton.click() + + refreshWorkSpace(wsWithMaskForRename, fixtureStack,remoteRobot) + + compressAndDecompressTree(zosUserDatasetMaskDoubleStar, fixtureStack, remoteRobot) + compressAndDecompressTree(defaultNewUssMask, fixtureStack, remoteRobot) + + checkItemWasDeletedWSRefreshed(zosUserDatasetMask, fixtureStack, remoteRobot) + checkItemWasDeletedWSRefreshed(ussMask, fixtureStack, remoteRobot) + + closableFixtureCollector.closeOnceIfExists(CreateMaskDialog.name) + closableFixtureCollector.closeOnceIfExists(CreateMaskDialog.name) + return deleteWSFromContextMenu(wsWithMaskForRename, fixtureStack, remoteRobot) } /** * Tests to rename mask to already exists, checks that correct message is returned. */ @Test - @Order(19) fun testRenameMaskToAlreadyExistsViaContextMenu(remoteRobot: RemoteRobot) = with(remoteRobot) { - val wsName = "first ws" - renameMaskFromContextMenu( - wsName, - "$ZOS_USERID.*".uppercase(), - "$ZOS_USERID.**", - false, - "Rename Dataset Mask", - remoteRobot - ) - renameMaskFromContextMenu(wsName, "/u", "/etc/ssh", false, "Rename USS Mask", remoteRobot) + // before test preparation + createWsWithConnectionFromAction(connectionName, alreadyExistsWorkingSetNameRename, fixtureStack, closableFixtureCollector, remoteRobot) + + createMask(alreadyExistsWorkingSetNameRename, zosUserDatasetMask, fixtureStack,closableFixtureCollector, ZOS_MASK, remoteRobot) + createMask(alreadyExistsWorkingSetNameRename, zosUserDatasetMaskDoubleStar, fixtureStack,closableFixtureCollector, ZOS_MASK, remoteRobot) + closableFixtureCollector.closeOnceIfExists(CreateMaskDialog.name) + createMask(alreadyExistsWorkingSetNameRename, ussMask, fixtureStack,closableFixtureCollector, USS_MASK, remoteRobot) + createMask(alreadyExistsWorkingSetNameRename, defaultNewUssMask, fixtureStack,closableFixtureCollector, USS_MASK, remoteRobot) + closableFixtureCollector.closeOnceIfExists(CreateMaskDialog.name) + + compressAndDecompressTree(alreadyExistsWorkingSetNameRename, fixtureStack, remoteRobot) + + //test + callEditWSFromContextMenu(zosUserDatasetMask, fixtureStack, remoteRobot) + renameDatasetMaskDialog.renameMaskFromContextMenu(zosUserDatasetMaskDoubleStar, remoteRobot) + okButton.click() + + assertFalse(okButton.isEnabled()) + val message = find(messageLoc,Duration.ofSeconds(30)).findAllText() + (message[0].text + message[1].text).shouldContain(UNIQUE_MASK.format(alreadyExistsWorkingSetNameRename, zosUserDatasetMask)) + cancelButton.click() + + callEditWSFromContextMenu(ussMask, fixtureStack, remoteRobot) + renameDatasetMaskDialog.renameMaskFromContextMenu(defaultNewUssMask, remoteRobot) + okButton.click() + + assertFalse(okButton.isEnabled()) + val messageUss = find(messageLoc,Duration.ofSeconds(30)).findAllText() + (messageUss[0].text + messageUss[1].text).shouldContain(UNIQUE_MASK.format(alreadyExistsWorkingSetNameRename, defaultNewUssMask)) + cancelButton.click() + + deleteWSFromContextMenu(alreadyExistsWorkingSetNameRename, fixtureStack, remoteRobot) + } /** * Tests to delete masks, checks that ws is refreshed in explorer and masks were deleted. */ @Test - @Order(20) - fun testDeleteMaskViaContextMenu(remoteRobot: RemoteRobot) = with(remoteRobot) { - val wsName = "new ws name" - deleteMaskFromContextMenu(wsName, "$ZOS_USERID.**".uppercase(), true, remoteRobot) - deleteMaskFromContextMenu(wsName, "/etc/ssh", false, remoteRobot) + fun testDeleteMaskViaContextMenu(remoteRobot: RemoteRobot) { + // before test preparation + createWsWithConnectionFromAction(connectionName, wsWithMaskForDelete, fixtureStack, closableFixtureCollector, remoteRobot) + + createMask(wsWithMaskForDelete, zosUserDatasetMask, fixtureStack,closableFixtureCollector, ZOS_MASK, remoteRobot) + closableFixtureCollector.closeOnceIfExists(CreateMaskDialog.name) + createMask(wsWithMaskForDelete, ussMask, fixtureStack,closableFixtureCollector, USS_MASK, remoteRobot) + closableFixtureCollector.closeOnceIfExists(CreateMaskDialog.name) + + compressAndDecompressTree(wsWithMaskForDelete, fixtureStack, remoteRobot) + + deletionOfUssPathRoot.dialogTitle = deletionOfUssPathRoot.dialogTitle.format(ussMask) + deletionOfDSMask.dialogTitle = deletionOfDSMask.dialogTitle.format(zosUserDatasetMask) + //test + + deleteWSFromContextMenu(ussMask, fixtureStack,remoteRobot) + deleteWSFromContextMenu(zosUserDatasetMask, fixtureStack,remoteRobot) + closableFixtureCollector.closeOnceIfExists(CreateMaskDialog.name) + return deleteWSFromContextMenu(wsWithMaskForDelete, fixtureStack, remoteRobot) } +} + + +@TestMethodOrder(MethodOrderer.OrderAnnotation::class) +@TestInstance(TestInstance.Lifecycle.PER_CLASS) +@ExtendWith(RemoteRobotExtension::class) +class WorkingSetViaContextMenuNoConnectionTest : WorkingSetBase(){ + private var closableFixtureCollector = ClosableFixtureCollector() + private var fixtureStack = mutableListOf() + private var wantToClose = mutableListOf("Add Working Set Dialog", "Edit Working Set Dialog", "Create Mask Dialog") +// private var addWorkingSetDialog = AddWorkingSetSubDialog() + override val wsName = "first ws" /** - * Deletes mask from working set via context menu. Checks that info is refreshed in explorer. + * Opens the project and Explorer, clears test environment. */ - private fun deleteMaskFromContextMenu( - wsName: String, - maskName: String, - isZOSMaskType: Boolean, - remoteRobot: RemoteRobot - ) = with(remoteRobot) { - val dialogTitle = if (isZOSMaskType) { - "Deletion of DS Mask $maskName" - } else { - "Deletion of Uss Path Root $maskName" - } - ideFrameImpl(projectName, fixtureStack) { - explorer { - fileExplorer.click() - find(viewTree).findText(wsName).doubleClick() - Thread.sleep(3000) - find(viewTree).findText(maskName).rightClick() - } - actionMenuItem(remoteRobot, "Delete").click() - dialog(dialogTitle) { - clickButton("Yes") - } - checkItemWasDeletedWSRefreshed(maskName, projectName, fixtureStack, remoteRobot) - explorer { - find(viewTree).findText(wsName).doubleClick() - } - } + @BeforeAll + fun setUpAll(remoteRobot: RemoteRobot) { + addWorkingSetDialog = AddWorkingSetSubDialog(fixtureStack, remoteRobot) + startMockServer() + setUpTestEnvironment(fixtureStack, closableFixtureCollector, remoteRobot) + } + /** + * Closes the project and clears test environment. + */ + @AfterAll + fun tearDownAll(remoteRobot: RemoteRobot) { + mockServer.shutdown() + clearEnvironment(fixtureStack, closableFixtureCollector, remoteRobot) + return closeIntelligentProject(fixtureStack, remoteRobot) } /** - * Creates mask in working set via context menu. + * Closes all unclosed closable fixtures that we want to close. */ - private fun createMaskFromContextMenu( - wsName: String, - maskName: String, - maskType: String, - remoteRobot: RemoteRobot - ) = - with(remoteRobot) { - ideFrameImpl(projectName, fixtureStack) { - createMask(wsName, fixtureStack, closableFixtureCollector) - createMaskDialog(fixtureStack) { - createMask(Pair(maskName, maskType)) - Thread.sleep(3000) - clickButton("OK") - } - closableFixtureCollector.closeOnceIfExists(CreateMaskDialog.name) - } - } + @AfterEach + fun tearDown(remoteRobot: RemoteRobot) { + responseDispatcher.removeAllEndpoints() + closableFixtureCollector.closeWantedClosables(wantToClose, remoteRobot) + } /** - * Renames mask in working set via context menu. + * Tests to add new working set without connection, checks that correct message is returned. */ - private fun renameMaskFromContextMenu( - wsName: String, - oldMaskName: String, - newMaskName: String, - isNewMaskUnique: Boolean, - dialogTitle: String, - remoteRobot: RemoteRobot - ) = + @Test + @Disabled("https://jira.ibagroup.eu/browse/IJMP-977") + fun testAddWorkingSetWithoutConnectionViaContextMenu(testInfo: TestInfo, remoteRobot: RemoteRobot) = with(remoteRobot) { - ideFrameImpl(projectName, fixtureStack) { - explorer { - fileExplorer.click() - find(viewTree).findText(wsName).doubleClick() - Thread.sleep(3000) - find(viewTree).findText(oldMaskName).rightClick() - } - actionMenuItem(remoteRobot, "Edit").click() - dialog(dialogTitle) { - find(byXpath("//div[@class='JBTextField']")).text = newMaskName + injectTestInfo(testInfo) + injectTestInfoRestTopology(testInfo) + + callCreateWSFromContextMenu(fixtureStack, remoteRobot) + try { + if (addWorkingSetDialog.isShown()) { + Assertions.assertTrue(false) } - clickButton("OK") - if (!isNewMaskUnique) { - assertFalse(button("OK").isEnabled()) - val message = find( - byXpath("//div[@class='HeavyWeightWindow']"), - Duration.ofSeconds(30) - ).findAllText() - (message[0].text + message[1].text).shouldContain("You must provide unique mask in working set. Working Set \"$wsName\" already has mask - $newMaskName") - clickButton("Cancel") + } catch (e: WaitForConditionTimeoutException) { + e.message.shouldContain("Failed to find 'Dialog' by 'Title Add Working Set Dialog'") + } finally { + closableFixtureCollector.closeOnceIfExists(AddWorkingSetDialog.name) + } + + ideFrameImpl(PROJECT_NAME, fixtureStack) { + createConnectionFromActionButton(closableFixtureCollector, fixtureStack) + addConnectionDialog(fixtureStack) { + addConnection( + connectionName, + "https://${mockServer.hostName}:${mockServer.port}", + ZOS_USERID, + ZOS_PWD, + true + ) + clickButton("OK") } - explorer { - find(viewTree).findText(wsName).doubleClick() + closableFixtureCollector.closeOnceIfExists(AddConnectionDialog.name) + + createWSFromContextMenu(fixtureStack, closableFixtureCollector) + addWorkingSetDialog(fixtureStack) { + addWorkingSet(wsName, connectionName) + clickButton("OK") + Thread.sleep(3000) + find(byXpath("//div[@class='HeavyWeightWindow']")).findText( + EMPTY_DATASET_MESSAGE + ) + clickButton("OK") + Thread.sleep(3000) } + closableFixtureCollector.closeOnceIfExists(AddWorkingSetDialog.name) } } + } diff --git a/src/uiTest/kotlin/workingset/WorkingSetViaSettingsTest.kt b/src/uiTest/kotlin/workingset/WorkingSetViaSettingsTest.kt index 6b761dee4..c307ca82c 100644 --- a/src/uiTest/kotlin/workingset/WorkingSetViaSettingsTest.kt +++ b/src/uiTest/kotlin/workingset/WorkingSetViaSettingsTest.kt @@ -49,7 +49,7 @@ class WorkingSetViaSettingsTest { @BeforeAll fun setUpAll(remoteRobot: RemoteRobot) { startMockServer() - setUpTestEnvironment(projectName, fixtureStack, closableFixtureCollector, remoteRobot) + setUpTestEnvironment(fixtureStack, closableFixtureCollector, remoteRobot) } /** @@ -58,8 +58,8 @@ class WorkingSetViaSettingsTest { @AfterAll fun tearDownAll(remoteRobot: RemoteRobot) = with(remoteRobot) { mockServer.shutdown() - clearEnvironment(projectName, fixtureStack, closableFixtureCollector, remoteRobot) - ideFrameImpl(projectName, fixtureStack) { + clearEnvironment(fixtureStack, closableFixtureCollector, remoteRobot) + ideFrameImpl(PROJECT_NAME, fixtureStack) { close() } } @@ -79,7 +79,7 @@ class WorkingSetViaSettingsTest { @Test @Order(1) fun testAddWorkingSetWithoutConnectionViaSettings(remoteRobot: RemoteRobot) = with(remoteRobot) { - ideFrameImpl(projectName, fixtureStack) { + ideFrameImpl(PROJECT_NAME, fixtureStack) { explorer { settings(closableFixtureCollector, fixtureStack) } @@ -121,7 +121,6 @@ class WorkingSetViaSettingsTest { { MockResponse().setBody(responseDispatcher.readMockJson("infoResponse") ?: "") } ) createConnection( - projectName, fixtureStack, closableFixtureCollector, connectionName, @@ -130,7 +129,7 @@ class WorkingSetViaSettingsTest { "https://${mockServer.hostName}:${mockServer.port}" ) val wsName: String = "A".repeat(200) - ideFrameImpl(projectName, fixtureStack) { + ideFrameImpl(PROJECT_NAME, fixtureStack) { explorer { settings(closableFixtureCollector, fixtureStack) } @@ -164,7 +163,7 @@ class WorkingSetViaSettingsTest { fun testAddWorkingSetWithOneValidMaskViaSettings(remoteRobot: RemoteRobot) = with(remoteRobot) { val wsName = "WS1" val mask = Pair("$ZOS_USERID.*", "z/OS") - ideFrameImpl(projectName, fixtureStack) { + ideFrameImpl(PROJECT_NAME, fixtureStack) { explorer { settings(closableFixtureCollector, fixtureStack) } @@ -206,7 +205,7 @@ class WorkingSetViaSettingsTest { { MockResponse().setBody("{}") } ) - ideFrameImpl(projectName, fixtureStack) { + ideFrameImpl(PROJECT_NAME, fixtureStack) { explorer { settings(closableFixtureCollector, fixtureStack) } @@ -229,7 +228,6 @@ class WorkingSetViaSettingsTest { openWSOpenMaskInExplorer( wsName, it.uppercase(), - projectName, fixtureStack, remoteRobot ) @@ -255,7 +253,7 @@ class WorkingSetViaSettingsTest { { MockResponse().setBody("{}") } ) - ideFrameImpl(projectName, fixtureStack) { + ideFrameImpl(PROJECT_NAME, fixtureStack) { explorer { settings(closableFixtureCollector, fixtureStack) } @@ -274,7 +272,7 @@ class WorkingSetViaSettingsTest { } closableFixtureCollector.closeOnceIfExists(SettingsDialog.name) } - validUSSMasks.forEach { openWSOpenMaskInExplorer(wsName, it, projectName, fixtureStack, remoteRobot) } + validUSSMasks.forEach { openWSOpenMaskInExplorer(wsName, it, fixtureStack, remoteRobot) } } @@ -285,7 +283,7 @@ class WorkingSetViaSettingsTest { @Order(6) fun testAddWorkingSetWithInvalidMasksViaSettings(remoteRobot: RemoteRobot) = with(remoteRobot) { val wsName = "WS4" - ideFrameImpl(projectName, fixtureStack) { + ideFrameImpl(PROJECT_NAME, fixtureStack) { explorer { settings(closableFixtureCollector, fixtureStack) } @@ -332,7 +330,7 @@ class WorkingSetViaSettingsTest { @Order(7) fun testAddWorkingSetWithTheSameMasksViaSettings(remoteRobot: RemoteRobot) = with(remoteRobot) { val wsName = "WS4" - ideFrameImpl(projectName, fixtureStack) { + ideFrameImpl(PROJECT_NAME, fixtureStack) { explorer { settings(closableFixtureCollector, fixtureStack) } @@ -372,9 +370,9 @@ class WorkingSetViaSettingsTest { { it?.requestLine?.contains("zosmf/restfiles/") ?: false }, { MockResponse().setBody("{}") } ) - openOrCloseWorkingSetInExplorer(wsName, projectName, fixtureStack, remoteRobot) - closeMaskInExplorer("$ZOS_USERID.*".uppercase(), projectName, fixtureStack, remoteRobot) - ideFrameImpl(projectName, fixtureStack) { + openOrCloseWorkingSetInExplorer(wsName, fixtureStack, remoteRobot) + closeMaskInExplorer("$ZOS_USERID.*".uppercase(), fixtureStack, remoteRobot) + ideFrameImpl(PROJECT_NAME, fixtureStack) { explorer { settings(closableFixtureCollector, fixtureStack) } @@ -393,8 +391,8 @@ class WorkingSetViaSettingsTest { } closableFixtureCollector.closeOnceIfExists(SettingsDialog.name) } - openMaskInExplorer("/u/$ZOS_USERID", "", projectName, fixtureStack, remoteRobot) - openOrCloseWorkingSetInExplorer(wsName, projectName, fixtureStack, remoteRobot) + openMaskInExplorer("/u/$ZOS_USERID", "", fixtureStack, remoteRobot) + openOrCloseWorkingSetInExplorer(wsName, fixtureStack, remoteRobot) } /** @@ -405,8 +403,8 @@ class WorkingSetViaSettingsTest { fun testEditWorkingSetDeleteMasksViaSettings(remoteRobot: RemoteRobot) = with(remoteRobot) { val wsName = "WS2" val masks = listOf("$ZOS_USERID.*".uppercase(), "Q.*", ZOS_USERID.uppercase()) - openOrCloseWorkingSetInExplorer(wsName, projectName, fixtureStack, remoteRobot) - ideFrameImpl(projectName, fixtureStack) { + openOrCloseWorkingSetInExplorer(wsName, fixtureStack, remoteRobot) + ideFrameImpl(PROJECT_NAME, fixtureStack) { explorer { settings(closableFixtureCollector, fixtureStack) } @@ -425,8 +423,8 @@ class WorkingSetViaSettingsTest { } closableFixtureCollector.closeOnceIfExists(SettingsDialog.name) } - masks.forEach { checkItemWasDeletedWSRefreshed(it.uppercase(), projectName, fixtureStack, remoteRobot) } - openOrCloseWorkingSetInExplorer(wsName, projectName, fixtureStack, remoteRobot) + masks.forEach { checkItemWasDeletedWSRefreshed(it.uppercase(), fixtureStack, remoteRobot) } + openOrCloseWorkingSetInExplorer(wsName, fixtureStack, remoteRobot) } /** @@ -437,8 +435,8 @@ class WorkingSetViaSettingsTest { fun testEditWorkingSetDeleteAllMasksViaSettings(remoteRobot: RemoteRobot) = with(remoteRobot) { val wsName = "WS2" val deletedMasks = listOf("$ZOS_USERID.**", "$ZOS_USERID.@#%", "$ZOS_USERID.@#%.*", "WWW.*", maskWithLength44) - openOrCloseWorkingSetInExplorer(wsName, projectName, fixtureStack, remoteRobot) - ideFrameImpl(projectName, fixtureStack) { + openOrCloseWorkingSetInExplorer(wsName, fixtureStack, remoteRobot) + ideFrameImpl(PROJECT_NAME, fixtureStack) { explorer { settings(closableFixtureCollector, fixtureStack) } @@ -459,8 +457,8 @@ class WorkingSetViaSettingsTest { } closableFixtureCollector.closeOnceIfExists(SettingsDialog.name) } - deletedMasks.forEach { checkItemWasDeletedWSRefreshed(it.uppercase(), projectName, fixtureStack, remoteRobot) } - openOrCloseWorkingSetInExplorer(wsName, projectName, fixtureStack, remoteRobot) + deletedMasks.forEach { checkItemWasDeletedWSRefreshed(it.uppercase(), fixtureStack, remoteRobot) } + openOrCloseWorkingSetInExplorer(wsName, fixtureStack, remoteRobot) } /** @@ -484,7 +482,6 @@ class WorkingSetViaSettingsTest { { MockResponse().setBody("{}") } ) createConnection( - projectName, fixtureStack, closableFixtureCollector, newConnectionName, @@ -492,8 +489,8 @@ class WorkingSetViaSettingsTest { remoteRobot, "https://${mockServer.hostName}:$testPort" ) - openOrCloseWorkingSetInExplorer(wsName, projectName, fixtureStack, remoteRobot) - ideFrameImpl(projectName, fixtureStack) { + openOrCloseWorkingSetInExplorer(wsName, fixtureStack, remoteRobot) + ideFrameImpl(PROJECT_NAME, fixtureStack) { explorer { settings(closableFixtureCollector, fixtureStack) } @@ -521,7 +518,6 @@ class WorkingSetViaSettingsTest { openMaskInExplorer( "$ZOS_USERID.*".uppercase(), "Invalid URL port: \"${testPort}1\"", - projectName, fixtureStack, remoteRobot ) @@ -546,7 +542,6 @@ class WorkingSetViaSettingsTest { { MockResponse().setBody(responseDispatcher.readMockJson("infoResponse") ?: "") } ) createConnection( - projectName, fixtureStack, closableFixtureCollector, newConnectionName, @@ -560,7 +555,7 @@ class WorkingSetViaSettingsTest { { it?.requestLine?.contains("zosmf/restfiles") ?: false }, { MockResponse().setBody("{}") } ) - ideFrameImpl(projectName, fixtureStack) { + ideFrameImpl(PROJECT_NAME, fixtureStack) { explorer { settings(closableFixtureCollector, fixtureStack) } @@ -579,8 +574,8 @@ class WorkingSetViaSettingsTest { } closableFixtureCollector.closeOnceIfExists(SettingsDialog.name) } - checkItemWasDeletedWSRefreshed("Invalid URL port: \"104431\"", projectName, fixtureStack, remoteRobot) - openOrCloseWorkingSetInExplorer(wsName, projectName, fixtureStack, remoteRobot) + checkItemWasDeletedWSRefreshed("Invalid URL port: \"104431\"", fixtureStack, remoteRobot) + openOrCloseWorkingSetInExplorer(wsName, fixtureStack, remoteRobot) } /** @@ -592,8 +587,8 @@ class WorkingSetViaSettingsTest { val newWorkingSetName = "new ws name" val oldWorkingSetName = "WS1" val alreadyExistsWorkingSetName = "WS2" - openOrCloseWorkingSetInExplorer(oldWorkingSetName, projectName, fixtureStack, remoteRobot) - ideFrameImpl(projectName, fixtureStack) { + openOrCloseWorkingSetInExplorer(oldWorkingSetName, fixtureStack, remoteRobot) + ideFrameImpl(PROJECT_NAME, fixtureStack) { explorer { settings(closableFixtureCollector, fixtureStack) } @@ -619,8 +614,8 @@ class WorkingSetViaSettingsTest { } closableFixtureCollector.closeOnceIfExists(SettingsDialog.name) } - checkItemWasDeletedWSRefreshed(oldWorkingSetName, projectName, fixtureStack, remoteRobot) - openOrCloseWorkingSetInExplorer(newWorkingSetName, projectName, fixtureStack, remoteRobot) + checkItemWasDeletedWSRefreshed(oldWorkingSetName, fixtureStack, remoteRobot) + openOrCloseWorkingSetInExplorer(newWorkingSetName, fixtureStack, remoteRobot) } /** @@ -630,7 +625,7 @@ class WorkingSetViaSettingsTest { @Order(14) fun testDeleteWorkingSetViaSettings(remoteRobot: RemoteRobot) = with(remoteRobot) { val wsName = "WS2" - ideFrameImpl(projectName, fixtureStack) { + ideFrameImpl(PROJECT_NAME, fixtureStack) { explorer { settings(closableFixtureCollector, fixtureStack) } @@ -643,7 +638,7 @@ class WorkingSetViaSettingsTest { } closableFixtureCollector.closeOnceIfExists(SettingsDialog.name) } - checkItemWasDeletedWSRefreshed(wsName, projectName, fixtureStack, remoteRobot) + checkItemWasDeletedWSRefreshed(wsName, fixtureStack, remoteRobot) } /** @@ -652,7 +647,7 @@ class WorkingSetViaSettingsTest { @Test @Order(15) fun testDeleteAllWorkingSetsViaSettings(remoteRobot: RemoteRobot) = with(remoteRobot) { - ideFrameImpl(projectName, fixtureStack) { + ideFrameImpl(PROJECT_NAME, fixtureStack) { explorer { settings(closableFixtureCollector, fixtureStack) }