From 9cfc7d28123ea719e3da94ea00206ebd782bc0b0 Mon Sep 17 00:00:00 2001 From: Nikita Katkov Date: Sun, 9 Aug 2026 23:48:53 +0200 Subject: [PATCH] [air] IJAI-241 fix AirSpecReferencesTest GitOrigin-RevId: 62f229d9f20e72ba41fe030e492f03d88debe6d8 --- .../wm/ex/ProjectFrameTypeServiceTest.kt | 148 ++++++++++++++++++ 1 file changed, 148 insertions(+) create mode 100644 platform/platform-tests/testSrc/com/intellij/openapi/wm/ex/ProjectFrameTypeServiceTest.kt diff --git a/platform/platform-tests/testSrc/com/intellij/openapi/wm/ex/ProjectFrameTypeServiceTest.kt b/platform/platform-tests/testSrc/com/intellij/openapi/wm/ex/ProjectFrameTypeServiceTest.kt new file mode 100644 index 000000000000..5a70401ca111 --- /dev/null +++ b/platform/platform-tests/testSrc/com/intellij/openapi/wm/ex/ProjectFrameTypeServiceTest.kt @@ -0,0 +1,148 @@ +// Copyright 2000-2026 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +package com.intellij.openapi.wm.ex + +import com.intellij.openapi.actionSystem.ActionPlaces +import com.intellij.openapi.util.JDOMUtil +import com.intellij.testFramework.ApplicationRule +import com.intellij.testFramework.DisposableRule +import com.intellij.testFramework.ExtensionTestUtil +import com.intellij.testFramework.LoggedErrorProcessor +import com.intellij.util.xmlb.XmlSerializer +import org.assertj.core.api.Assertions.assertThat +import org.junit.ClassRule +import org.junit.Rule +import org.junit.Test + +class ProjectFrameTypeServiceTest { + companion object { + @ClassRule + @JvmField + val appRule = ApplicationRule() + } + + @Rule + @JvmField + val disposableRule = DisposableRule() + + @Test + fun `descriptor deserializes plugin xml notation`() { + val bean = XmlSerializer.deserialize( + JDOMUtil.load(""" + + + + + """.trimIndent()), + ProjectFrameTypeBean::class.java, + ) + + assertThat(bean.id).isEqualTo("AGENT_DEDICATED") + assertThat(bean.reopenWhenHidden).isTrue() + assertThat(bean.toolWindowLayoutProfile).isEqualTo("air.dedicated") + assertThat(bean.excludedActions.map { it.place to it.actionId }) + .containsExactly("MainToolbar" to "MainToolbarVCSGroup", "MainToolbar" to "NewUiRunWidget") + } + + @Test + fun `descriptor defaults are inert`() { + val bean = XmlSerializer.deserialize(JDOMUtil.load(""""""), ProjectFrameTypeBean::class.java) + + assertThat(bean.reopenWhenHidden).isFalse() + assertThat(bean.toolWindowLayoutProfile).isNull() + assertThat(bean.excludedActions).isEmpty() + } + + @Test + fun `policy is resolved by frame type id`() { + val service = maskDescriptors( + descriptor("DEDICATED") { + reopenWhenHidden = true + toolWindowLayoutProfile = "dedicated" + excludedActions = listOf( + exclusion(ActionPlaces.MAIN_TOOLBAR, "MainToolbarVCSGroup"), + exclusion(ActionPlaces.MAIN_TOOLBAR, "NewUiRunWidget"), + exclusion("OtherPlace", "SomethingElse"), + ) + }, + descriptor("PLAIN"), + ) + + assertThat(service.findDescriptor("DEDICATED")?.id).isEqualTo("DEDICATED") + assertThat(service.canReopenWhenHidden("DEDICATED")).isTrue() + assertThat(service.getToolWindowLayoutProfileId("DEDICATED")).isEqualTo("dedicated") + assertThat(service.getExcludedActionIds("DEDICATED", ActionPlaces.MAIN_TOOLBAR)) + .containsExactly("MainToolbarVCSGroup", "NewUiRunWidget") + + assertThat(service.canReopenWhenHidden("PLAIN")).isFalse() + assertThat(service.getToolWindowLayoutProfileId("PLAIN")).isNull() + assertThat(service.getExcludedActionIds("PLAIN", ActionPlaces.MAIN_TOOLBAR)).isEmpty() + } + + @Test + fun `undeclared and blank frame types resolve to no policy`() { + val service = maskDescriptors(descriptor("DEDICATED") { reopenWhenHidden = true }) + + for (frameTypeId in listOf(null, "", " ", "UNKNOWN")) { + assertThat(service.findDescriptor(frameTypeId)).describedAs(frameTypeId ?: "null").isNull() + assertThat(service.canReopenWhenHidden(frameTypeId)).describedAs(frameTypeId ?: "null").isFalse() + assertThat(service.getToolWindowLayoutProfileId(frameTypeId)).describedAs(frameTypeId ?: "null").isNull() + assertThat(service.getExcludedActionIds(frameTypeId, ActionPlaces.MAIN_TOOLBAR)).describedAs(frameTypeId ?: "null").isEmpty() + } + } + + @Test + fun `declared keys are trimmed and blanks are dropped`() { + val service = maskDescriptors( + descriptor(" DEDICATED ") { + toolWindowLayoutProfile = " dedicated " + excludedActions = listOf( + exclusion(" ${ActionPlaces.MAIN_TOOLBAR} ", " MainToolbarVCSGroup "), + exclusion(ActionPlaces.MAIN_TOOLBAR, " "), + ) + } + ) + + assertThat(service.getToolWindowLayoutProfileId("DEDICATED")).isEqualTo("dedicated") + assertThat(service.getExcludedActionIds(" DEDICATED ", ActionPlaces.MAIN_TOOLBAR)).containsExactly("MainToolbarVCSGroup") + assertThat(service.getExcludedActionIds("DEDICATED", " ")).isEmpty() + } + + @Test + fun `duplicate ids keep the first descriptor and report the clash`() { + val service = maskDescriptors( + descriptor("DEDICATED") { toolWindowLayoutProfile = "first" }, + descriptor("DEDICATED") { toolWindowLayoutProfile = "second" }, + ) + + val loggedErrors = ArrayList() + LoggedErrorProcessor.executeWith(object : LoggedErrorProcessor() { + override fun processError(category: String, message: String, details: Array, t: Throwable?): MutableSet { + loggedErrors.add(message) + return Action.NONE + } + }) { + assertThat(service.getToolWindowLayoutProfileId("DEDICATED")).isEqualTo("first") + } + + assertThat(loggedErrors).singleElement().asString().contains("DEDICATED") + } + + private fun maskDescriptors(vararg beans: ProjectFrameTypeBean): ProjectFrameTypeService { + ExtensionTestUtil.maskExtensions(ProjectFrameTypeService.EP_NAME, beans.toList(), disposableRule.disposable) + return ProjectFrameTypeService() + } +} + +private fun descriptor(id: String, configure: ProjectFrameTypeBean.() -> Unit = {}): ProjectFrameTypeBean { + return ProjectFrameTypeBean().also { + it.id = id + it.configure() + } +} + +private fun exclusion(place: String, actionId: String): ProjectFrameActionExclusionBean { + return ProjectFrameActionExclusionBean().also { + it.place = place + it.actionId = actionId + } +}