Skip to content

Commit d90a78d

Browse files
committedJan 28, 2025·
fix: show success notification when pushing multi-resource editor (#710)
Signed-off-by: Andre Dietisheim <[email protected]>
1 parent 92f4e7e commit d90a78d

File tree

16 files changed

+681
-122
lines changed

16 files changed

+681
-122
lines changed
 

Diff for: ‎src/main/kotlin/com/redhat/devtools/intellij/kubernetes/editor/EditorFocusListener.kt

+5-2
Original file line numberDiff line numberDiff line change
@@ -61,9 +61,12 @@ class EditorFocusListener(private val project: Project) : FileEditorManagerListe
6161
editor: FileEditor,
6262
project: Project
6363
) {
64-
ErrorNotification(editor, project).show(
64+
val notification = ErrorNotification(editor, project)
65+
notification.show(
6566
e.message ?: "Undefined error",
66-
e)
67+
e,
68+
{ notification.hide() }
69+
)
6770
}
6871

6972
}

Diff for: ‎src/main/kotlin/com/redhat/devtools/intellij/kubernetes/editor/EditorResourceState.kt

+71-9
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111
******************************************************************************/
1212
package com.redhat.devtools.intellij.kubernetes.editor
1313

14+
import java.util.Objects
15+
1416
val FILTER_ALL = { _: EditorResource -> true }
1517
val FILTER_TO_PUSH = { editorResource: EditorResource ->
1618
val state = editorResource.getState()
@@ -25,16 +27,66 @@ val FILTER_PUSHED = { editorResource: EditorResource ->
2527
editorResource.getState() is Pushed
2628
}
2729

28-
abstract class EditorResourceState
30+
abstract class EditorResourceState {
31+
override fun equals(other: Any?): Boolean {
32+
if (this === other) {
33+
return true
34+
}
35+
if (javaClass != other?.javaClass) {
36+
return false
37+
}
38+
return true
39+
}
40+
41+
override fun hashCode(): Int {
42+
return Objects.hash()
43+
}
44+
}
2945

3046
class Error(val title: String, val message: String? = null): EditorResourceState() {
3147
constructor(title: String, e: Throwable) : this(title, e.message)
48+
49+
override fun equals(other: Any?): Boolean {
50+
if (this === other) {
51+
return true
52+
}
53+
return other is Error
54+
&& title == other.title
55+
&& message == other.message
56+
}
57+
58+
override fun hashCode(): Int {
59+
return Objects.hash(
60+
title,
61+
message
62+
)
63+
}
3264
}
3365

3466
open class Identical: EditorResourceState()
3567

3668
abstract class Different(val exists: Boolean, val isOutdatedVersion: Boolean): EditorResourceState() {
3769
abstract fun isPush(): Boolean
70+
override fun equals(other: Any?): Boolean {
71+
if (this === other) {
72+
return true
73+
}
74+
if (javaClass != other?.javaClass) {
75+
return false
76+
}
77+
return other is Different
78+
&& other.exists == exists
79+
&& other.isOutdatedVersion == isOutdatedVersion
80+
&& other.isPush() == isPush()
81+
}
82+
83+
override fun hashCode(): Int {
84+
return Objects.hash(
85+
exists,
86+
isOutdatedVersion,
87+
isPush()
88+
)
89+
}
3890
}
3991

4092
open class Modified(exists: Boolean, isOutdatedVersion: Boolean): Different(exists, isOutdatedVersion) {
@@ -43,23 +95,33 @@ open class Modified(exists: Boolean, isOutdatedVersion: Boolean): Different(exis
4395

4496
class DeletedOnCluster: Modified(false, false) {
4597
override fun isPush() = true
46-
4798
}
4899

49100
class Outdated: Different(true, true) {
50101
override fun isPush() = false
51-
52102
}
53103

54104
abstract class Pushed: Identical() {
55105
abstract val updated: Boolean
56-
}
57106

58-
class Created: Pushed() {
59-
override val updated = false
60-
}
61-
class Updated: Pushed() {
62-
override val updated = true
107+
override fun equals(other: Any?): Boolean {
108+
if (this === other) {
109+
return true
110+
}
111+
if (javaClass != other?.javaClass) {
112+
return false
113+
}
114+
return other is Pushed
115+
&& other.updated == updated
116+
}
117+
118+
override fun hashCode(): Int {
119+
return Objects.hashCode(updated)
120+
}
63121
}
64122

123+
class Created(override val updated: Boolean = false) : Pushed()
124+
125+
class Updated(override val updated: Boolean = true): Pushed()
126+
65127
class Pulled: EditorResourceState()

Diff for: ‎src/main/kotlin/com/redhat/devtools/intellij/kubernetes/editor/ResourceEditorFactory.kt

+2-1
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,8 @@ open class ResourceEditorFactory protected constructor(
127127
editor.file?.putUserData(ResourceEditor.KEY_RESOURCE_EDITOR, resourceEditor)
128128
resourceEditor
129129
} catch (e: ResourceException) {
130-
ErrorNotification(editor, project).show(e.message ?: "", e.cause?.message)
130+
val notification = ErrorNotification(editor, project)
131+
notification.show(e.message ?: "", e.cause?.message, { notification.hide() })
131132
runAsync { telemetry.error(e).send() }
132133
null
133134
}

Diff for: ‎src/main/kotlin/com/redhat/devtools/intellij/kubernetes/editor/notification/DeletedNotification.kt

+9-9
Original file line numberDiff line numberDiff line change
@@ -10,41 +10,41 @@
1010
******************************************************************************/
1111
package com.redhat.devtools.intellij.kubernetes.editor.notification
1212

13+
import com.intellij.icons.AllIcons
1314
import com.intellij.openapi.fileEditor.FileEditor
1415
import com.intellij.openapi.project.Project
1516
import com.intellij.openapi.util.Key
1617
import com.intellij.ui.EditorNotificationPanel
1718
import com.redhat.devtools.intellij.kubernetes.editor.hideNotification
1819
import com.redhat.devtools.intellij.kubernetes.editor.showNotification
1920
import com.redhat.devtools.intellij.kubernetes.model.util.toKindAndName
21+
import icons.Icons
2022
import io.fabric8.kubernetes.api.model.HasMetadata
2123
import javax.swing.JComponent
2224

2325
/**
2426
* An editor (panel) notification that informs about a deleted resource on the cluster.
2527
*/
26-
class DeletedNotification(private val editor: FileEditor, private val project: Project) {
28+
open class DeletedNotification(private val editor: FileEditor, private val project: Project) {
2729

28-
companion object {
30+
private companion object {
2931
private val KEY_PANEL = Key<JComponent>(DeletedNotification::class.java.canonicalName)
3032
}
3133

32-
fun show(resource: HasMetadata) {
33-
editor.showNotification(KEY_PANEL, { createPanel(resource) }, project)
34+
fun show(resource: HasMetadata, closeAction: () -> Unit) {
35+
editor.showNotification(KEY_PANEL, { createPanel(resource, closeAction) }, project)
3436
}
3537

3638
fun hide() {
3739
editor.hideNotification(KEY_PANEL, project)
3840
}
3941

40-
private fun createPanel(resource: HasMetadata): EditorNotificationPanel {
42+
private fun createPanel(resource: HasMetadata, hideAction: () -> Unit): EditorNotificationPanel {
4143
val panel = EditorNotificationPanel()
4244
panel.text = "${toKindAndName(resource)} was deleted on cluster. Push to Cluster?"
45+
panel.icon(Icons.upload)
4346
addPush(false, panel)
44-
addDismiss(panel) {
45-
hide()
46-
}
47-
47+
addHide(panel, hideAction)
4848
return panel
4949
}
5050
}

Diff for: ‎src/main/kotlin/com/redhat/devtools/intellij/kubernetes/editor/notification/ErrorNotification.kt

+12-9
Original file line numberDiff line numberDiff line change
@@ -26,30 +26,33 @@ import javax.swing.JComponent
2626
*/
2727
class ErrorNotification(private val editor: FileEditor, private val project: Project) {
2828

29-
companion object {
29+
private companion object {
3030
private val KEY_PANEL = Key<JComponent>(ErrorNotification::class.java.canonicalName)
3131
}
3232

33-
fun show(title: String, message: String?) {
34-
editor.showNotification(KEY_PANEL, { createPanel(editor, title, message) }, project)
33+
fun show(title: String, message: String?, closeAction: (() -> Unit)? = null) {
34+
editor.showNotification(KEY_PANEL, { createPanel(editor, title, message, closeAction) }, project)
3535
}
3636

37-
fun show(title: String, e: Throwable) {
38-
editor.showNotification(KEY_PANEL, { createPanel(editor, title, e.message) }, project)
37+
fun show(title: String, e: Throwable, closeAction: (() -> Unit)? = null) {
38+
editor.showNotification(KEY_PANEL, { createPanel(editor, title, e.message, closeAction) }, project)
3939
}
4040

4141
fun hide() {
4242
editor.hideNotification(KEY_PANEL, project)
4343
}
4444

45-
private fun createPanel(editor: FileEditor, title: String, message: String?): EditorNotificationPanel {
45+
private fun createPanel(
46+
editor: FileEditor,
47+
title: String,
48+
message: String?,
49+
closeAction: (() -> Unit)?
50+
): EditorNotificationPanel {
4651
val panel = EditorNotificationPanel()
4752
panel.icon(AllIcons.Ide.FatalError)
4853
panel.text = title
4954
addDetailsAction(message, panel, editor)
50-
addDismiss(panel) {
51-
hide()
52-
}
55+
addHide(panel, closeAction)
5356
return panel
5457
}
5558

Diff for: ‎src/main/kotlin/com/redhat/devtools/intellij/kubernetes/editor/notification/NotificationActions.kt

+4-2
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,10 @@ fun addPull(panel: EditorNotificationPanel) {
1818
panel.createActionLabel("Pull", PullAction.ID)
1919
}
2020

21-
fun addDismiss(panel: EditorNotificationPanel, consumer: () -> Unit) {
22-
panel.createActionLabel("Dismiss", consumer)
21+
fun addHide(panel: EditorNotificationPanel, closeAction: (() -> Unit)?) {
22+
if (closeAction != null) {
23+
panel.setCloseAction(closeAction)
24+
}
2325
}
2426

2527
fun addDiff(panel: EditorNotificationPanel) {

Diff for: ‎src/main/kotlin/com/redhat/devtools/intellij/kubernetes/editor/notification/Notifications.kt

+151-43
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,15 @@ import com.intellij.openapi.fileEditor.FileEditor
1515
import com.intellij.openapi.project.Project
1616
import com.redhat.devtools.intellij.kubernetes.editor.DeletedOnCluster
1717
import com.redhat.devtools.intellij.kubernetes.editor.EditorResource
18+
import com.redhat.devtools.intellij.kubernetes.editor.EditorResourceState
1819
import com.redhat.devtools.intellij.kubernetes.editor.Error
1920
import com.redhat.devtools.intellij.kubernetes.editor.FILTER_ERROR
21+
import com.redhat.devtools.intellij.kubernetes.editor.FILTER_PUSHED
2022
import com.redhat.devtools.intellij.kubernetes.editor.FILTER_TO_PUSH
2123
import com.redhat.devtools.intellij.kubernetes.editor.Modified
24+
import com.redhat.devtools.intellij.kubernetes.editor.Pulled
2225
import com.redhat.devtools.intellij.kubernetes.editor.Pushed
23-
import io.fabric8.kubernetes.api.model.HasMetadata
26+
import com.redhat.devtools.intellij.kubernetes.model.util.HasMetadataIdentifier
2427

2528
open class Notifications(
2629
editor: FileEditor,
@@ -39,33 +42,63 @@ open class Notifications(
3942
private val errorNotification: ErrorNotification = ErrorNotification(editor, project),
4043
) {
4144

45+
private val dismissed = mutableMapOf<HasMetadataIdentifier, EditorResourceState>()
46+
4247
fun show(editorResource: EditorResource) {
4348
show(editorResource, true)
4449
}
4550

51+
/**
52+
* Shows the notification for the given [EditorResource].
53+
* Notifications are mutually exclusive, only a single notification is shown at a time.
54+
* There's a precedence in notifications:
55+
* * Error overrides everything else
56+
* * Pushed overrides Pulled, DeletedOnCluster, Push
57+
* * Pulled override DeletedOnCluster, Push
58+
* * DeletedOnCluster override Push
59+
*
60+
* @param editorResource the resource to show the notification for
61+
* @param showSyncNotifications whether to show a Push notification.
62+
*
63+
* @see EditorResourceState
64+
*/
4665
fun show(editorResource: EditorResource, showSyncNotifications: Boolean) {
4766
val state = editorResource.getState()
4867
val resource = editorResource.getResource()
49-
when {
68+
when {
5069
state is Error ->
51-
showError(state.title, state.message)
70+
hideAllAndRun {
71+
errorNotification.show(state.title, state.message, { errorNotification.hide() })
72+
}
5273

5374
state is Pushed ->
54-
showPushed(listOf(editorResource))
75+
hideAllAndRun {
76+
pushedNotification.show(listOf(editorResource), { pushedNotification.hide() })
77+
}
78+
79+
state is Pulled ->
80+
hideAllAndRun {
81+
pulledNotification.show(resource, { pulledNotification.hide() })
82+
}
5583

5684
state is DeletedOnCluster
5785
&& showSyncNotifications ->
58-
showDeleted(resource)
86+
hideAllAndRun {
87+
deletedNotification.show(resource, { deletedNotification.hide() })
88+
}
5989

6090
/**
6191
* avoid too many notifications, don't notify outdated
62-
state is Outdated && showSyncNotification ->
63-
showPullNotification(resource)
92+
*
93+
state is Outdated && showSyncNotification ->
94+
showPullNotification(resource)
6495
*/
6596

6697
state is Modified
6798
&& showSyncNotifications ->
68-
showPush(true, listOf(editorResource))
99+
hideAllAndRun {
100+
pushNotification.show(true, listOf(editorResource), { pushNotification.hide() })
101+
}
69102

70103
else ->
71104
hideAll()
@@ -76,65 +109,129 @@ open class Notifications(
76109
show(editorResources, true)
77110
}
78111

112+
/**
113+
* Shows notifications for the given [EditorResource]s.
114+
* Notifications are stacked vertically but only the first notification has a dismiss button/action.
115+
*
116+
* @param editorResources the resources to show notifications for
117+
* @param showSyncNotifications whether to show a push notification
118+
*
119+
* @see EditorResourceState
120+
*/
79121
fun show(editorResources: Collection<EditorResource>, showSyncNotifications: Boolean) {
80-
val toPush = editorResources.filter(FILTER_TO_PUSH)
81-
if (toPush.isNotEmpty()
82-
&& showSyncNotifications) {
83-
showPush(false, toPush)
84-
return
85-
}
86-
val inError = editorResources.filter(FILTER_ERROR)
87-
if (inError.isNotEmpty()) {
88-
showError(inError)
89-
} else {
122+
removeUpdatedDismissed(editorResources)
123+
val inError = getNonDismissed(editorResources.filter(FILTER_ERROR))
124+
val showError = inError.isNotEmpty();
125+
val pushed = getNonDismissed(editorResources.filter(FILTER_PUSHED))
126+
val showPushed = pushed.isNotEmpty()
127+
val toPush = getNonDismissed(editorResources.filter(FILTER_TO_PUSH))
128+
val showToPush = toPush.isNotEmpty() && showSyncNotifications
129+
val dismissAction = {
130+
dismiss(editorResources)
90131
hideAll()
91132
}
92-
}
93133

94-
fun showError(title: String, message: String?) {
95134
runInUI {
96135
hideAll()
97-
errorNotification.show(title, message)
136+
if (showError) {
137+
showError(
138+
inError,
139+
dismissAction
140+
)
141+
}
142+
if (showPushed) {
143+
showPushed(
144+
pushed,
145+
// dismiss action only if there's no error-notification
146+
!showError,
147+
dismissAction
148+
)
149+
}
150+
if (showToPush) {
151+
showPush(
152+
toPush,
153+
// dismiss action only if there's no error- nor pushed-notification
154+
!showError && !showPushed,
155+
dismissAction
156+
)
157+
}
98158
}
99159
}
100160

101-
private fun showError(editorResources: Collection<EditorResource>) {
102-
val inError = editorResources.filter(FILTER_ERROR)
103-
val toDisplay = inError.firstOrNull()?.getState() as? Error ?: return
104-
showError(toDisplay.title, toDisplay.message)
161+
private fun showError(inError: Collection<EditorResource>, dismissAction: (() -> Unit)?) {
162+
val error = inError.firstOrNull()?.getState() as? Error ?: return
163+
errorNotification.show(error.title, error.message, dismissAction)
164+
}
165+
166+
private fun showPushed(pushed: Collection<EditorResource>, showDismissAction: Boolean, dismissAction: () -> Unit) {
167+
pushedNotification.show(
168+
pushed,
169+
if (showDismissAction) dismissAction else null
170+
)
105171
}
106172

107-
private fun showPush(showPull: Boolean, editorResources: Collection<EditorResource>) {
173+
private fun showPush(toPush: Collection<EditorResource>, showDismissAction: Boolean, dismissAction: () -> Unit) {
174+
pushNotification.show(
175+
false,
176+
toPush,
177+
if (showDismissAction) dismissAction else null
178+
)
179+
}
180+
181+
/**
182+
* Show an error notification for the given title and message.
183+
*
184+
* @param title the title of the notification
185+
* @param message the message of the notification
186+
*
187+
* @see EditorResourceState
188+
*/
189+
fun showError(title: String, message: String?) {
108190
runInUI {
109-
// hide & show in the same UI thread runnable avoid flickering
110191
hideAll()
111-
pushNotification.show(showPull, editorResources)
192+
errorNotification.show(title, message, { errorNotification.hide() })
112193
}
113194
}
114195

115-
private fun showPushed(editorResources: Collection<EditorResource>) {
116-
runInUI {
117-
// hide & show in the same UI thread runnable avoid flickering
118-
hideAll()
119-
pushedNotification.show(editorResources)
196+
/** for testing purposes **/
197+
protected open fun dismiss(resources: Collection<EditorResource>) {
198+
resources.forEach { resource ->
199+
dismissed[HasMetadataIdentifier(resource.getResource())] = resource.getState()
120200
}
121201
}
122202

123-
private fun showPull(resource: HasMetadata) {
124-
runInUI {
125-
hideAll()
126-
pullNotification.show(resource)
203+
private fun isDismissedButUpdated(editorResource: EditorResource): Boolean {
204+
val dismissed = dismissed[HasMetadataIdentifier(editorResource.getResource())] ?: return false
205+
return editorResource.getState() != dismissed
206+
}
207+
208+
private fun isDismissed(editorResource: EditorResource): Boolean {
209+
return dismissed[HasMetadataIdentifier(editorResource.getResource())] != null
210+
}
211+
212+
private fun getNonDismissed(editorResources: Collection<EditorResource>): Collection<EditorResource> {
213+
return editorResources.filter { editorResource ->
214+
!isDismissed(editorResource)
127215
}
128216
}
129217

130-
private fun showDeleted(resource: HasMetadata) {
131-
runInUI {
132-
// hide & show in the same UI thread runnable avoid flickering
133-
hideAll()
134-
deletedNotification.show(resource)
218+
private fun removeUpdatedDismissed(editorResources: Collection<EditorResource>) {
219+
editorResources.forEach { editorResource ->
220+
if (isDismissedButUpdated(editorResource)) {
221+
dismissed.remove(HasMetadataIdentifier(editorResource.getResource()))
222+
}
135223
}
136224
}
137225

226+
/**
227+
* Hides the following notifications, leaving the other ones alone if they are shown.
228+
*
229+
* * Push
230+
* * Pull
231+
* * Deleted
232+
*
233+
* @see EditorResourceState
234+
*/
138235
fun hideSyncNotifications() {
139236
runInUI {
140237
pushNotification.hide()
@@ -143,17 +240,28 @@ open class Notifications(
143240
}
144241
}
145242

243+
/**
244+
* Hides all notifications if they're shown.
245+
*/
146246
fun hideAll() {
147247
runInUI {
148248
pushNotification.hide()
149249
pushedNotification.hide()
150250
pullNotification.hide()
151-
deletedNotification.hide()
152251
pulledNotification.hide()
252+
deletedNotification.hide()
153253
errorNotification.hide()
154254
}
155255
}
156256

257+
private fun hideAllAndRun(runnable: () -> Unit) {
258+
runInUI {
259+
// hide & show in the same UI thread runnable avoid flickering
260+
hideAll()
261+
runnable.invoke()
262+
}
263+
}
264+
157265
protected open fun runInUI(runnable: () -> Unit) {
158266
if (ApplicationManager.getApplication().isDispatchThread) {
159267
runnable.invoke()

Diff for: ‎src/main/kotlin/com/redhat/devtools/intellij/kubernetes/editor/notification/PullNotification.kt

+8-7
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,15 @@
1010
******************************************************************************/
1111
package com.redhat.devtools.intellij.kubernetes.editor.notification
1212

13+
import com.intellij.icons.AllIcons
1314
import com.intellij.openapi.fileEditor.FileEditor
1415
import com.intellij.openapi.project.Project
1516
import com.intellij.openapi.util.Key
1617
import com.intellij.ui.EditorNotificationPanel
1718
import com.redhat.devtools.intellij.kubernetes.editor.hideNotification
1819
import com.redhat.devtools.intellij.kubernetes.editor.showNotification
1920
import com.redhat.devtools.intellij.kubernetes.model.util.toKindAndName
21+
import icons.Icons
2022
import io.fabric8.kubernetes.api.model.HasMetadata
2123
import javax.swing.JComponent
2224

@@ -26,27 +28,26 @@ import javax.swing.JComponent
2628
*/
2729
class PullNotification(private val editor: FileEditor, private val project: Project) {
2830

29-
companion object {
31+
private companion object {
3032
val KEY_PANEL = Key<JComponent>(PullNotification::class.java.canonicalName)
3133
}
3234

33-
fun show(resource: HasMetadata) {
34-
editor.showNotification(KEY_PANEL, { createPanel(resource) }, project)
35+
fun show(resource: HasMetadata, closeAction: (() -> Unit)?) {
36+
editor.showNotification(KEY_PANEL, { createPanel(resource, closeAction) }, project)
3537
}
3638

3739
fun hide() {
3840
editor.hideNotification(KEY_PANEL, project)
3941
}
4042

41-
private fun createPanel(resource: HasMetadata): EditorNotificationPanel {
43+
private fun createPanel(resource: HasMetadata, closeAction: (() -> Unit)?): EditorNotificationPanel {
4244
val panel = EditorNotificationPanel()
4345
panel.text = "${toKindAndName(resource)} changed on cluster. Pull?"
46+
panel.icon(Icons.download)
4447
addPull(panel)
4548
addPush(true, panel)
4649
addDiff(panel)
47-
addDismiss(panel) {
48-
hide()
49-
}
50+
addHide(panel, closeAction)
5051
return panel
5152
}
5253
}

Diff for: ‎src/main/kotlin/com/redhat/devtools/intellij/kubernetes/editor/notification/PulledNotification.kt

+7-7
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
******************************************************************************/
1111
package com.redhat.devtools.intellij.kubernetes.editor.notification
1212

13+
import com.intellij.icons.AllIcons
1314
import com.intellij.openapi.fileEditor.FileEditor
1415
import com.intellij.openapi.project.Project
1516
import com.intellij.openapi.util.Key
@@ -24,19 +25,19 @@ import javax.swing.JComponent
2425
*/
2526
class PulledNotification(private val editor: FileEditor, private val project: Project) {
2627

27-
companion object {
28+
private companion object {
2829
private val KEY_PANEL = Key<JComponent>(PulledNotification::class.java.canonicalName)
2930
}
3031

31-
fun show(resource: HasMetadata) {
32-
editor.showNotification(KEY_PANEL, { createPanel(resource) }, project)
32+
fun show(resource: HasMetadata, closeAction: (() -> Unit)?) {
33+
editor.showNotification(KEY_PANEL, { createPanel(resource, closeAction) }, project)
3334
}
3435

3536
fun hide() {
3637
editor.hideNotification(KEY_PANEL, project)
3738
}
3839

39-
private fun createPanel(resource: HasMetadata): EditorNotificationPanel {
40+
private fun createPanel(resource: HasMetadata, closeAction: (() -> Unit)?): EditorNotificationPanel {
4041
val panel = EditorNotificationPanel()
4142
panel.text =
4243
"Pulled ${resource.kind} '${resource.metadata.name}' ${
@@ -46,9 +47,8 @@ class PulledNotification(private val editor: FileEditor, private val project: Pr
4647
""
4748
}
4849
}"
49-
addDismiss(panel) {
50-
hide()
51-
}
50+
panel.icon(AllIcons.RunConfigurations.ShowPassed)
51+
addHide(panel, closeAction)
5252
return panel
5353
}
5454
}

Diff for: ‎src/main/kotlin/com/redhat/devtools/intellij/kubernetes/editor/notification/PushNotification.kt

+12-10
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
******************************************************************************/
1111
package com.redhat.devtools.intellij.kubernetes.editor.notification
1212

13+
import com.intellij.icons.AllIcons
1314
import com.intellij.openapi.fileEditor.FileEditor
1415
import com.intellij.openapi.project.Project
1516
import com.intellij.openapi.util.Key
@@ -19,18 +20,19 @@ import com.redhat.devtools.intellij.kubernetes.editor.EditorResource
1920
import com.redhat.devtools.intellij.kubernetes.editor.hideNotification
2021
import com.redhat.devtools.intellij.kubernetes.editor.showNotification
2122
import com.redhat.devtools.intellij.kubernetes.model.util.toKindAndNames
23+
import icons.Icons
2224
import javax.swing.JComponent
2325

2426
/**
2527
* An editor (panel) notification that informs of a change in the editor that may be pushed to the cluster.
2628
*/
2729
class PushNotification(private val editor: FileEditor, private val project: Project) {
2830

29-
companion object {
31+
private companion object {
3032
val KEY_PANEL = Key<JComponent>(PushNotification::class.java.canonicalName)
3133
}
3234

33-
fun show(showPull: Boolean, editorResources: Collection<EditorResource>) {
35+
fun show(showPull: Boolean, editorResources: Collection<EditorResource>, closeAction: (() -> Unit)? = null) {
3436
val toCreateOrUpdate = editorResources
3537
.filter { editorResource ->
3638
editorResource.getState() is Different
@@ -44,7 +46,7 @@ class PushNotification(private val editor: FileEditor, private val project: Proj
4446
&& toUpdate.isEmpty()) {
4547
return
4648
}
47-
editor.showNotification(KEY_PANEL, { createPanel(showPull, toCreate, toUpdate) }, project)
49+
editor.showNotification(KEY_PANEL, { createPanel(showPull, toCreate, toUpdate, closeAction) }, project)
4850
}
4951

5052
fun hide() {
@@ -54,14 +56,16 @@ class PushNotification(private val editor: FileEditor, private val project: Proj
5456
private fun createPanel(
5557
showPull: Boolean,
5658
toCreate: Collection<EditorResource>,
57-
toUpdate: Collection<EditorResource>
59+
toUpdate: Collection<EditorResource>,
60+
closeAction: (() -> Unit)?
5861
): EditorNotificationPanel {
5962
val text = createText(toCreate, toUpdate)
6063
return createPanel(text,
6164
toUpdate.isNotEmpty(),
6265
showPull && toUpdate.any {
6366
editorResource -> editorResource.isOutdatedVersion()
64-
})
67+
},
68+
closeAction)
6569
}
6670

6771
private fun createText(toCreate: Collection<EditorResource>?, toUpdate: Collection<EditorResource>?): String {
@@ -82,8 +86,9 @@ class PushNotification(private val editor: FileEditor, private val project: Proj
8286
.toString()
8387
}
8488

85-
private fun createPanel(text: String, existsOnCluster: Boolean, isOutdated: Boolean): EditorNotificationPanel {
89+
private fun createPanel(text: String, existsOnCluster: Boolean, isOutdated: Boolean, closeAction: (() -> Unit)?): EditorNotificationPanel {
8690
val panel = EditorNotificationPanel()
91+
panel.icon(Icons.upload)
8792
panel.text = text
8893
addPush(false, panel)
8994
if (isOutdated) {
@@ -92,10 +97,7 @@ class PushNotification(private val editor: FileEditor, private val project: Proj
9297
if (existsOnCluster) {
9398
addDiff(panel)
9499
}
95-
addDismiss(panel) {
96-
hide()
97-
}
98-
100+
addHide(panel, closeAction)
99101
return panel
100102
}
101103
}

Diff for: ‎src/main/kotlin/com/redhat/devtools/intellij/kubernetes/editor/notification/PushedNotification.kt

+7-8
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
******************************************************************************/
1111
package com.redhat.devtools.intellij.kubernetes.editor.notification
1212

13+
import com.intellij.icons.AllIcons
1314
import com.intellij.openapi.fileEditor.FileEditor
1415
import com.intellij.openapi.project.Project
1516
import com.intellij.openapi.util.Key
@@ -27,22 +28,22 @@ import javax.swing.JComponent
2728
*/
2829
class PushedNotification(private val editor: FileEditor, private val project: Project) {
2930

30-
companion object {
31+
private companion object {
3132
val KEY_PANEL = Key<JComponent>(PushedNotification::class.java.canonicalName)
3233
}
3334

34-
fun show(editorResources: Collection<EditorResource>) {
35+
fun show(editorResources: Collection<EditorResource>, closeAction: (() -> Unit)? = null) {
3536
if (editorResources.isEmpty()) {
3637
return
3738
}
38-
editor.showNotification(KEY_PANEL, { createPanel(editorResources) }, project)
39+
editor.showNotification(KEY_PANEL, { createPanel(editorResources, closeAction) }, project)
3940
}
4041

4142
fun hide() {
4243
editor.hideNotification(KEY_PANEL, project)
4344
}
4445

45-
private fun createPanel(editorResources: Collection<EditorResource>): EditorNotificationPanel {
46+
private fun createPanel(editorResources: Collection<EditorResource>, closeAction: (() -> Unit)?): EditorNotificationPanel {
4647
val panel = EditorNotificationPanel()
4748
val createdOrUpdated = editorResources
4849
.filter(FILTER_PUSHED)
@@ -52,10 +53,8 @@ class PushedNotification(private val editor: FileEditor, private val project: Pr
5253
val created = createdOrUpdated[false]
5354
val updated = createdOrUpdated[true]
5455
panel.text = createText(created, updated)
55-
addDismiss(panel) {
56-
hide()
57-
}
58-
56+
panel.icon(AllIcons.RunConfigurations.ShowPassed)
57+
addHide(panel, closeAction)
5958
return panel
6059
}
6160

Diff for: ‎src/main/kotlin/com/redhat/devtools/intellij/kubernetes/model/util/ResourceUtils.kt

+14
Original file line numberDiff line numberDiff line change
@@ -431,4 +431,18 @@ fun String?.toBooleanOrNull(): Boolean? {
431431
return null
432432
}
433433
return this.toBoolean()
434+
}
435+
436+
data class HasMetadataIdentifier private constructor(
437+
private val kind: String,
438+
private val apiVersion: String,
439+
private val name: String,
440+
private val namespace: String?
441+
) {
442+
constructor(resource: HasMetadata) : this(
443+
resource.kind,
444+
resource.apiVersion,
445+
resource.metadata.name,
446+
resource.metadata.namespace
447+
)
434448
}

Diff for: ‎src/main/resources/META-INF/plugin.xml

+1-1
Original file line numberDiff line numberDiff line change
@@ -225,7 +225,7 @@
225225
<codeInsight.inlayProvider language="yaml"
226226
implementationClass="com.redhat.devtools.intellij.kubernetes.editor.inlay.Base64ValueInlayHintsProvider"
227227
id="MarkdownTableInlayProvider"/>
228-
<applicationConfigurable id="tools.settings.redhat.kubernetes"
228+
<projectConfigurable id="tools.settings.redhat.kubernetes"
229229
parentId="tools"
230230
displayName="Red Hat Kubernetes"
231231
instance="com.redhat.devtools.intellij.kubernetes.settings.SettingsConfigurable" />

Diff for: ‎src/test/kotlin/com/redhat/devtools/intellij/kubernetes/editor/EditorResourceStateTest.kt

+261
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,267 @@ class EditorResourceStateTest {
106106
assertThat(toResources(filtered)).isEmpty()
107107
}
108108

109+
@Test
110+
fun `Error is not equal to all the other states`() {
111+
// given
112+
// when
113+
assertThat(Error("yoda", "is a jedi")).isEqualTo(Error("yoda", "is a jedi"))
114+
assertThat(Error("yoda", "is a green gnome")).isNotEqualTo(Error("yoda", "is a jedi"))
115+
assertThat(Error("obiwan", "is a jedi")).isNotEqualTo(Error("yoda", "is a jedi"))
116+
117+
assertThat(Error("yoda", "is a jedi")).isNotEqualTo(Identical())
118+
119+
assertThat(Error("yoda", "is a jedi")).isNotEqualTo(Modified(true, true))
120+
assertThat(Error("yoda", "is a jedi")).isNotEqualTo(Modified(true, false))
121+
assertThat(Error("yoda", "is a jedi")).isNotEqualTo(Modified(false, false))
122+
123+
assertThat(Error("yoda", "is a jedi")).isNotEqualTo(DeletedOnCluster())
124+
125+
assertThat(Error("yoda", "is a jedi")).isNotEqualTo(Outdated())
126+
127+
assertThat(Error("yoda", "is a jedi")).isNotEqualTo(Created(true))
128+
assertThat(Error("yoda", "is a jedi")).isNotEqualTo(Created(false))
129+
130+
assertThat(Error("yoda", "is a jedi")).isNotEqualTo(Updated(true))
131+
assertThat(Error("yoda", "is a jedi")).isNotEqualTo(Updated(false))
132+
133+
assertThat(Error("yoda", "is a jedi")).isNotEqualTo(Pulled())
134+
}
135+
136+
@Test
137+
fun `Identical is not equal to all the other states`() {
138+
// given
139+
// when
140+
assertThat(Identical()).isNotEqualTo(Error("yoda", "is a jedi"))
141+
142+
assertThat(Identical()).isEqualTo(Identical())
143+
144+
assertThat(Identical()).isNotEqualTo(Modified(true, true))
145+
assertThat(Identical()).isNotEqualTo(Modified(true, false))
146+
assertThat(Identical()).isNotEqualTo(Modified(false, false))
147+
148+
assertThat(Identical()).isNotEqualTo(DeletedOnCluster())
149+
150+
assertThat(Identical()).isNotEqualTo(Outdated())
151+
152+
assertThat(Identical()).isNotEqualTo(Created(true))
153+
assertThat(Identical()).isNotEqualTo(Created(false))
154+
155+
assertThat(Identical()).isNotEqualTo(Updated(true))
156+
assertThat(Identical()).isNotEqualTo(Updated(false))
157+
158+
assertThat(Identical()).isNotEqualTo(Pulled())
159+
}
160+
161+
@Test
162+
fun `Modified is not equal to all the other states`() {
163+
// given
164+
// when
165+
assertThat(Modified(true, true)).isNotEqualTo(Error("yoda", "is a jedi"))
166+
assertThat(Modified(true, false)).isNotEqualTo(Error("yoda", "is a jedi"))
167+
assertThat(Modified(false, false)).isNotEqualTo(Error("yoda", "is a jedi"))
168+
assertThat(Modified(false, true)).isNotEqualTo(Error("yoda", "is a jedi"))
169+
170+
assertThat(Modified(true, true)).isNotEqualTo(Identical())
171+
assertThat(Modified(true, false)).isNotEqualTo(Identical())
172+
assertThat(Modified(false, false)).isNotEqualTo(Identical())
173+
assertThat(Modified(false, true)).isNotEqualTo(Identical())
174+
175+
assertThat(Modified(true, true)).isEqualTo(Modified(true, true))
176+
assertThat(Modified(true, true)).isNotEqualTo(Modified(true, false))
177+
assertThat(Modified(true, true)).isNotEqualTo(Modified(false, true))
178+
assertThat(Modified(true, true)).isNotEqualTo(Modified(false, false))
179+
180+
assertThat(Modified(true, true)).isNotEqualTo(DeletedOnCluster())
181+
assertThat(Modified(true, false)).isNotEqualTo(DeletedOnCluster())
182+
assertThat(Modified(false, true)).isNotEqualTo(DeletedOnCluster())
183+
assertThat(Modified(false, false)).isNotEqualTo(DeletedOnCluster())
184+
185+
assertThat(Modified(true, true)).isNotEqualTo(Outdated())
186+
assertThat(Modified(true, false)).isNotEqualTo(Outdated())
187+
assertThat(Modified(false, true)).isNotEqualTo(Outdated())
188+
assertThat(Modified(false, false)).isNotEqualTo(Outdated())
189+
190+
assertThat(Modified(true, true)).isNotEqualTo(Created(true))
191+
assertThat(Modified(true, false)).isNotEqualTo(Created(true))
192+
assertThat(Modified(false, true)).isNotEqualTo(Created(true))
193+
assertThat(Modified(false, false)).isNotEqualTo(Created(true))
194+
assertThat(Modified(true, true)).isNotEqualTo(Created(false))
195+
assertThat(Modified(true, false)).isNotEqualTo(Created(false))
196+
assertThat(Modified(false, true)).isNotEqualTo(Created(false))
197+
assertThat(Modified(false, false)).isNotEqualTo(Created(false))
198+
199+
assertThat(Modified(true, true)).isNotEqualTo(Updated(true))
200+
assertThat(Modified(true, false)).isNotEqualTo(Updated(true))
201+
assertThat(Modified(false, true)).isNotEqualTo(Updated(true))
202+
assertThat(Modified(false, false)).isNotEqualTo(Updated(true))
203+
assertThat(Modified(true, true)).isNotEqualTo(Updated(false))
204+
assertThat(Modified(true, false)).isNotEqualTo(Updated(false))
205+
assertThat(Modified(false, true)).isNotEqualTo(Updated(false))
206+
assertThat(Modified(false, false)).isNotEqualTo(Updated(false))
207+
208+
assertThat(Modified(true, true)).isNotEqualTo(Pulled())
209+
assertThat(Modified(true, false)).isNotEqualTo(Pulled())
210+
assertThat(Modified(false, true)).isNotEqualTo(Pulled())
211+
assertThat(Modified(false, false)).isNotEqualTo(Pulled())
212+
}
213+
214+
@Test
215+
fun `DeletedOnCluster is not equal to all the other states`() {
216+
// given
217+
// when
218+
assertThat(DeletedOnCluster()).isNotEqualTo(Error("darth vader", "is on the dark side"))
219+
220+
assertThat(DeletedOnCluster()).isNotEqualTo(Identical())
221+
222+
assertThat(DeletedOnCluster()).isNotEqualTo(Modified(true, true))
223+
assertThat(DeletedOnCluster()).isNotEqualTo(Modified(true, false))
224+
assertThat(DeletedOnCluster()).isNotEqualTo(Modified(false, false))
225+
assertThat(DeletedOnCluster()).isNotEqualTo(Modified(false, true))
226+
227+
assertThat(DeletedOnCluster()).isEqualTo(DeletedOnCluster())
228+
229+
assertThat(DeletedOnCluster()).isNotEqualTo(Outdated())
230+
231+
assertThat(DeletedOnCluster()).isNotEqualTo(Created(true))
232+
assertThat(DeletedOnCluster()).isNotEqualTo(Created(false))
233+
234+
assertThat(DeletedOnCluster()).isNotEqualTo(Updated(true))
235+
assertThat(DeletedOnCluster()).isNotEqualTo(Updated(false))
236+
237+
assertThat(DeletedOnCluster()).isNotEqualTo(Pulled())
238+
}
239+
240+
@Test
241+
fun `Outdated is not equal to all the other states`() {
242+
// given
243+
// when
244+
assertThat(Outdated()).isNotEqualTo(Error("obiwan", "is a jedi"))
245+
246+
assertThat(Outdated()).isNotEqualTo(Identical())
247+
248+
assertThat(Outdated()).isNotEqualTo(Modified(true, true))
249+
assertThat(Outdated()).isNotEqualTo(Modified(true, false))
250+
assertThat(Outdated()).isNotEqualTo(Modified(false, true))
251+
assertThat(Outdated()).isNotEqualTo(Modified(false, false))
252+
253+
assertThat(Outdated()).isNotEqualTo(DeletedOnCluster())
254+
255+
assertThat(Outdated()).isEqualTo(Outdated())
256+
257+
assertThat(Outdated()).isNotEqualTo(Created(true))
258+
assertThat(Outdated()).isNotEqualTo(Created(false))
259+
260+
assertThat(Outdated()).isNotEqualTo(Updated(true))
261+
assertThat(Outdated()).isNotEqualTo(Updated(false))
262+
263+
assertThat(Outdated()).isNotEqualTo(Pulled())
264+
}
265+
266+
@Test
267+
fun `Created is not equal to all the other states`() {
268+
// given
269+
// when
270+
assertThat(Created(true)).isNotEqualTo(Error("darth vader", "is on the dark side"))
271+
assertThat(Created(false)).isNotEqualTo(Error("darth vader", "is on the dark side"))
272+
273+
assertThat(Created(true)).isNotEqualTo(Identical())
274+
assertThat(Created(false)).isNotEqualTo(Identical())
275+
276+
assertThat(Created(true)).isNotEqualTo(Modified(true, true))
277+
assertThat(Created(true)).isNotEqualTo(Modified(true, false))
278+
assertThat(Created(true)).isNotEqualTo(Modified(false, false))
279+
assertThat(Created(true)).isNotEqualTo(Modified(false, true))
280+
assertThat(Created(false)).isNotEqualTo(Modified(true, true))
281+
assertThat(Created(false)).isNotEqualTo(Modified(true, false))
282+
assertThat(Created(false)).isNotEqualTo(Modified(false, false))
283+
assertThat(Created(false)).isNotEqualTo(Modified(false, true))
284+
285+
assertThat(Created(true)).isNotEqualTo(DeletedOnCluster())
286+
assertThat(Created(false)).isNotEqualTo(DeletedOnCluster())
287+
288+
assertThat(Created(true)).isNotEqualTo(Outdated())
289+
assertThat(Created(false)).isNotEqualTo(Outdated())
290+
291+
assertThat(Created(true)).isEqualTo(Created(true))
292+
assertThat(Created(true)).isNotEqualTo(Created(false))
293+
assertThat(Created(false)).isNotEqualTo(Created(true))
294+
assertThat(Created(false)).isEqualTo(Created(false))
295+
296+
assertThat(Created(true)).isNotEqualTo(Updated(true))
297+
assertThat(Created(true)).isNotEqualTo(Updated(false))
298+
assertThat(Created(false)).isNotEqualTo(Updated(true))
299+
assertThat(Created(false)).isNotEqualTo(Updated(false))
300+
301+
assertThat(Created(true)).isNotEqualTo(Pulled())
302+
assertThat(Created(false)).isNotEqualTo(Pulled())
303+
}
304+
305+
@Test
306+
fun `Updated is not equal to all the other states`() {
307+
// given
308+
// when
309+
assertThat(Updated(true)).isNotEqualTo(Error("darth vader", "is on the dark side"))
310+
assertThat(Updated(false)).isNotEqualTo(Error("darth vader", "is on the dark side"))
311+
312+
assertThat(Updated(true)).isNotEqualTo(Identical())
313+
assertThat(Updated(false)).isNotEqualTo(Identical())
314+
315+
assertThat(Updated(true)).isNotEqualTo(Modified(true, true))
316+
assertThat(Updated(true)).isNotEqualTo(Modified(true, false))
317+
assertThat(Updated(true)).isNotEqualTo(Modified(false, false))
318+
assertThat(Updated(true)).isNotEqualTo(Modified(false, true))
319+
assertThat(Updated(false)).isNotEqualTo(Modified(true, true))
320+
assertThat(Updated(false)).isNotEqualTo(Modified(true, false))
321+
assertThat(Updated(false)).isNotEqualTo(Modified(false, false))
322+
assertThat(Updated(false)).isNotEqualTo(Modified(false, true))
323+
324+
assertThat(Updated(true)).isNotEqualTo(DeletedOnCluster())
325+
assertThat(Updated(false)).isNotEqualTo(DeletedOnCluster())
326+
327+
assertThat(Updated(true)).isNotEqualTo(Outdated())
328+
assertThat(Updated(false)).isNotEqualTo(Outdated())
329+
330+
assertThat(Updated(true)).isNotEqualTo(Created(true))
331+
assertThat(Updated(true)).isNotEqualTo(Created(false))
332+
assertThat(Updated(false)).isNotEqualTo(Created(true))
333+
assertThat(Updated(false)).isNotEqualTo(Created(false))
334+
335+
assertThat(Updated(true)).isEqualTo(Updated(true))
336+
assertThat(Updated(true)).isNotEqualTo(Updated(false))
337+
assertThat(Updated(false)).isNotEqualTo(Updated(true))
338+
assertThat(Updated(false)).isEqualTo(Updated(false))
339+
340+
assertThat(Updated(true)).isNotEqualTo(Pulled())
341+
assertThat(Updated(false)).isNotEqualTo(Pulled())
342+
}
343+
344+
@Test
345+
fun `Pulled is not equal to all the other states`() {
346+
// given
347+
// when
348+
assertThat(Pulled()).isNotEqualTo(Error("darth vader", "is on the dark side"))
349+
350+
assertThat(Pulled()).isNotEqualTo(Identical())
351+
352+
assertThat(Pulled()).isNotEqualTo(Modified(true, true))
353+
assertThat(Pulled()).isNotEqualTo(Modified(true, false))
354+
assertThat(Pulled()).isNotEqualTo(Modified(false, false))
355+
assertThat(Pulled()).isNotEqualTo(Modified(false, true))
356+
357+
assertThat(Pulled()).isNotEqualTo(DeletedOnCluster())
358+
359+
assertThat(Pulled()).isNotEqualTo(Outdated())
360+
361+
assertThat(Pulled()).isNotEqualTo(Created(true))
362+
assertThat(Pulled()).isNotEqualTo(Created(false))
363+
364+
assertThat(Pulled()).isNotEqualTo(Updated(true))
365+
assertThat(Pulled()).isNotEqualTo(Updated(false))
366+
367+
assertThat(Pulled()).isEqualTo(Pulled())
368+
}
369+
109370
private fun createEditorResources(vararg resourceAndStates: Pair<HasMetadata, EditorResourceState>): Collection<EditorResource> {
110371
return resourceAndStates.map { resourceAndState ->
111372
createEditorResource(resourceAndState.first, resourceAndState.second)

Diff for: ‎src/test/kotlin/com/redhat/devtools/intellij/kubernetes/editor/inlay/Base64PresentationsTest.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ class Base64PresentationsTest {
2828
"skywalker", "light side", kubernetesTypeInfo("ConfigMap", "v1")
2929
)
3030
private val pod = kubernetesResourceInfo(
31-
"anakin", "dark side", kubernetesTypeInfo("Pod", "v1")
31+
"anakin", "light side", kubernetesTypeInfo("Pod", "v1")
3232
)
3333

3434
private val dataElement = createYAMLKeyValue("data")

Diff for: ‎src/test/kotlin/com/redhat/devtools/intellij/kubernetes/editor/notification/NotificationsTest.kt

+116-13
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,14 @@ package com.redhat.devtools.intellij.kubernetes.editor.notification
1313
import com.intellij.openapi.fileEditor.FileEditor
1414
import com.intellij.openapi.project.Project
1515
import com.nhaarman.mockitokotlin2.any
16+
import com.nhaarman.mockitokotlin2.anyOrNull
1617
import com.nhaarman.mockitokotlin2.argWhere
1718
import com.nhaarman.mockitokotlin2.doReturn
1819
import com.nhaarman.mockitokotlin2.eq
1920
import com.nhaarman.mockitokotlin2.mock
2021
import com.nhaarman.mockitokotlin2.never
2122
import com.nhaarman.mockitokotlin2.verify
23+
import com.nhaarman.mockitokotlin2.whenever
2224
import com.redhat.devtools.intellij.kubernetes.editor.Created
2325
import com.redhat.devtools.intellij.kubernetes.editor.DeletedOnCluster
2426
import com.redhat.devtools.intellij.kubernetes.editor.EditorResource
@@ -91,7 +93,7 @@ class NotificationsTest {
9193
// when
9294
notifications.show(resources)
9395
// then
94-
verify(pushNotification).show(eq(false), containsAll(darthVader))
96+
verify(pushNotification).show(eq(false), containsAll(darthVader), anyOrNull())
9597
}
9698

9799
@Test
@@ -104,7 +106,7 @@ class NotificationsTest {
104106
// when
105107
notifications.show(resources, false)
106108
// then
107-
verify(pushNotification, never()).show(any(), any())
109+
verify(pushNotification, never()).show(any(), any(), anyOrNull())
108110
}
109111

110112
@Test
@@ -118,7 +120,7 @@ class NotificationsTest {
118120
// when
119121
notifications.show(resources)
120122
// then
121-
verify(pushNotification).show(eq(false), containsAll(obiwan))
123+
verify(pushNotification).show(eq(false), containsAll(obiwan), anyOrNull())
122124
}
123125

124126
@Test
@@ -132,7 +134,7 @@ class NotificationsTest {
132134
// when
133135
notifications.show(resources, false)
134136
// then
135-
verify(pushNotification, never()).show(any(), any())
137+
verify(pushNotification, never()).show(any(), any(), anyOrNull())
136138
}
137139

138140
@Test
@@ -153,7 +155,7 @@ class NotificationsTest {
153155
// when
154156
notifications.show(resource)
155157
// then
156-
verify(deletedNotification).show(darthVader)
158+
verify(deletedNotification).show(eq(darthVader), any())
157159
}
158160

159161
@Test
@@ -164,7 +166,7 @@ class NotificationsTest {
164166
// when
165167
notifications.show(resource, false)
166168
// then
167-
verify(deletedNotification, never()).show(any())
169+
verify(deletedNotification, never()).show(any(), any())
168170
}
169171

170172
@Test
@@ -177,7 +179,7 @@ class NotificationsTest {
177179
// when
178180
notifications.show(resources, false)
179181
// then
180-
verify(pushNotification, never()).show(any(), any())
182+
verify(pushNotification, never()).show(any(), any(), anyOrNull())
181183
}
182184

183185
@Test
@@ -193,7 +195,36 @@ class NotificationsTest {
193195
// when
194196
notifications.show(resources)
195197
// then
196-
verify(pushNotification).show(eq(false), containsAll(emperor, darthVader))
198+
verify(pushNotification).show(eq(false), containsAll(emperor, darthVader), anyOrNull())
199+
}
200+
201+
@Test
202+
fun `#show should NOT show push notification with deleted resources if it was dismissed`() {
203+
// given
204+
val emperor = resource<Pod>("emperor")
205+
val resources = listOf(
206+
editorResource(emperor, DeletedOnCluster())
207+
)
208+
notifications.dismiss(resources)
209+
// when
210+
notifications.show(resources)
211+
// then
212+
verify(pushNotification, never()).show(eq(false), eq(resources), anyOrNull())
213+
}
214+
215+
@Test
216+
fun `#show should show push notification for resource if it was dismissed but eventually updated`() {
217+
// given
218+
val emperor = resource<Pod>("emperor")
219+
val editorResource = editorResource(emperor, DeletedOnCluster())
220+
val resources = listOf(editorResource)
221+
notifications.dismiss(resources)
222+
doReturn(Modified(false, false))
223+
.whenever(editorResource).getState()
224+
// when
225+
notifications.show(resources)
226+
// then
227+
verify(pushNotification).show(eq(false), eq(resources), anyOrNull())
197228
}
198229

199230
@Test
@@ -218,7 +249,38 @@ class NotificationsTest {
218249
// when
219250
notifications.show(resources)
220251
// then
221-
verify(errorNotification).show(title, message)
252+
verify(errorNotification).show(eq(title), eq(message), anyOrNull())
253+
}
254+
255+
@Test
256+
fun `#show should NOT show error notification if resource is in error but it was dismissed`() {
257+
// given
258+
val title = "dismissed disturbance in the force"
259+
val message = "need to meditate more"
260+
val darthVader = resource<Pod>("darth vader")
261+
val resources = listOf(editorResource(darthVader, Error(title, message)))
262+
notifications.dismiss(resources)
263+
// when
264+
notifications.show(resources)
265+
// then
266+
verify(errorNotification, never()).show(eq(title), eq(message), anyOrNull())
267+
}
268+
269+
@Test
270+
fun `#show should show error notification if resource is in error but it was dismissed but eventually modified`() {
271+
// given
272+
val title = "dismissed disturbance in the force"
273+
val darthVader = resource<Pod>("darth vader")
274+
val editorResource = editorResource(darthVader, Error(title))
275+
val resources = listOf(editorResource)
276+
notifications.dismiss(resources)
277+
val newTitle = "used the force but it failed"
278+
doReturn(Error(newTitle))
279+
.whenever(editorResource).getState()
280+
// when
281+
notifications.show(resources)
282+
// then
283+
verify(errorNotification).show(eq(newTitle), eq(null), anyOrNull())
222284
}
223285

224286
@Test
@@ -229,7 +291,19 @@ class NotificationsTest {
229291
// when
230292
notifications.show(resource, true)
231293
// then
232-
verify(pushedNotification).show(containsAll(luke))
294+
verify(pushedNotification).show(containsAll(luke), anyOrNull())
295+
}
296+
297+
@Test
298+
fun `#show NOT should show pushed notification if resource was created but was dismissed`() {
299+
// given
300+
val luke = resource<Pod>("luke")
301+
val resources = listOf(editorResource(luke, Created()))
302+
notifications.dismiss(resources)
303+
// when
304+
notifications.show(resources, true)
305+
// then
306+
verify(pushedNotification, never()).show(containsAll(luke), anyOrNull())
233307
}
234308

235309
@Test
@@ -240,7 +314,7 @@ class NotificationsTest {
240314
// when
241315
notifications.show(resource, false)
242316
// then
243-
verify(pushedNotification).show(containsAll(luke))
317+
verify(pushedNotification).show(containsAll(luke), anyOrNull())
244318
}
245319

246320
@Test
@@ -251,7 +325,7 @@ class NotificationsTest {
251325
// when
252326
notifications.show(resource, true)
253327
// then
254-
verify(pushedNotification).show(containsAll(r2d2))
328+
verify(pushedNotification).show(containsAll(r2d2), anyOrNull())
255329
}
256330

257331
@Test
@@ -262,7 +336,32 @@ class NotificationsTest {
262336
// when
263337
notifications.show(resource, false)
264338
// then
265-
verify(pushedNotification).show(containsAll(r2d2))
339+
verify(pushedNotification).show(containsAll(r2d2), anyOrNull())
340+
}
341+
342+
@Test
343+
fun `#hideAll should hide push-, pushed-, pull-, pulled-, deleted- and error-notification`() {
344+
// given
345+
// when
346+
notifications.hideAll()
347+
// then
348+
verify(pushNotification).hide()
349+
verify(pushedNotification).hide()
350+
verify(pullNotification).hide()
351+
verify(pulledNotification).hide()
352+
verify(deletedNotification).hide()
353+
verify(errorNotification).hide()
354+
}
355+
356+
@Test
357+
fun `#hideSyncNotifications should hide push-, pull- and deleted-notification`() {
358+
// given
359+
// when
360+
notifications.hideSyncNotifications()
361+
// then
362+
verify(pushNotification).hide()
363+
verify(pullNotification).hide()
364+
verify(deletedNotification).hide()
266365
}
267366

268367
private fun editorResource(resource: HasMetadata, state: EditorResourceState): EditorResource {
@@ -308,6 +407,10 @@ class NotificationsTest {
308407
deletedNotification,
309408
errorNotification
310409
) {
410+
public override fun dismiss(resources: Collection<EditorResource>) {
411+
super.dismiss(resources)
412+
}
413+
311414
override fun runInUI(runnable: () -> Unit) {
312415
// run directly
313416
runnable.invoke()

0 commit comments

Comments
 (0)
Please sign in to comment.