From 849f18bfd4d8eb0bf4252c7fd6d3a77789423953 Mon Sep 17 00:00:00 2001 From: Teodora Sandu <81559517+teodora-sandu@users.noreply.github.com> Date: Fri, 12 Jul 2024 09:55:44 +0100 Subject: [PATCH] feat: load html for oss in ls (#570) --- CHANGELOG.md | 1 + .../ui/jcef/OpenFileLoadHandlerGenerator.kt | 92 -------------- .../ui/jcef/ThemeBasedStylingGenerator.kt | 119 ++++++++++++++++++ .../kotlin/io/snyk/plugin/ui/jcef/Utils.kt | 11 +- .../toolwindow/panels/JCEFDescriptionPanel.kt | 44 ++++--- src/main/kotlin/snyk/common/lsp/Types.kt | 6 +- .../kotlin/stylesheets/SnykStylesheets.kt | 4 +- .../stylesheets/snyk_oss_suggestion.scss | 78 ++++++++++++ ...uggestionDescriptionPanelFromLSCodeTest.kt | 34 ++--- ...SuggestionDescriptionPanelFromLSOSSTest.kt | 29 ++++- 10 files changed, 274 insertions(+), 144 deletions(-) create mode 100644 src/main/kotlin/io/snyk/plugin/ui/jcef/ThemeBasedStylingGenerator.kt create mode 100644 src/main/resources/stylesheets/snyk_oss_suggestion.scss diff --git a/CHANGELOG.md b/CHANGELOG.md index ec68c036d..1d67a8dfd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ ### Added - Updated Open Source, Containers and IaC products to include `.snyk` in the list of supported build files. - When a `.snyk` file changes, the OSS cache will be dropped triggering a scan. +- Render the OSS suggestion panel using HTML from the Languag Server. ### Fixed - Refresh the OSS cache for Language Server scans when a file is changed. diff --git a/src/main/kotlin/io/snyk/plugin/ui/jcef/OpenFileLoadHandlerGenerator.kt b/src/main/kotlin/io/snyk/plugin/ui/jcef/OpenFileLoadHandlerGenerator.kt index 94ed97130..5d914fa22 100644 --- a/src/main/kotlin/io/snyk/plugin/ui/jcef/OpenFileLoadHandlerGenerator.kt +++ b/src/main/kotlin/io/snyk/plugin/ui/jcef/OpenFileLoadHandlerGenerator.kt @@ -1,22 +1,16 @@ package io.snyk.plugin.ui.jcef import com.intellij.openapi.application.ApplicationManager -import com.intellij.openapi.editor.colors.ColorKey import com.intellij.openapi.editor.colors.EditorColorsManager -import com.intellij.openapi.editor.colors.EditorColorsScheme -import com.intellij.openapi.editor.colors.FontPreferences import com.intellij.openapi.project.Project import com.intellij.openapi.vfs.VirtualFile import com.intellij.ui.jcef.JBCefBrowserBase import com.intellij.ui.jcef.JBCefJSQuery -import com.intellij.util.ui.JBUI -import com.intellij.util.ui.UIUtil import io.snyk.plugin.getDocument import io.snyk.plugin.navigateToSource import org.cef.browser.CefBrowser import org.cef.browser.CefFrame import org.cef.handler.CefLoadHandlerAdapter -import java.awt.Color class OpenFileLoadHandlerGenerator( private val project: Project, @@ -45,38 +39,6 @@ class OpenFileLoadHandlerGenerator( return JBCefJSQuery.Response("success") } - fun toCssHex(color: Color): String { - return "#%02x%02x%02x".format(color.red, color.green, color.blue) - } - - fun shift( - colorComponent: Int, - d: Double, - ): Int { - val n = (colorComponent * d).toInt() - return n.coerceIn(0, 255) - } - - fun getCodeDiffColors( - baseColor: Color, - isHighContrast: Boolean, - ): Pair { - val addedColor = - if (isHighContrast) { - Color(28, 68, 40) // high contrast green - } else { - Color(shift(baseColor.red, 0.75), baseColor.green, shift(baseColor.blue, 0.75)) - } - - val removedColor = - if (isHighContrast) { - Color(84, 36, 38) // high contrast red - } else { - Color(shift(baseColor.red, 1.25), shift(baseColor.green, 0.85), shift(baseColor.blue, 0.85)) - } - return Pair(addedColor, removedColor) - } - fun generate(jbCefBrowser: JBCefBrowserBase): CefLoadHandlerAdapter { val openFileQuery = JBCefJSQuery.create(jbCefBrowser) val isDarkTheme = EditorColorsManager.getInstance().isDarkEditor @@ -124,60 +86,6 @@ class OpenFileLoadHandlerGenerator( })(); """ browser.executeJavaScript(script, browser.url, 0) - - val baseColor = UIUtil.getTextFieldBackground() - val (addedColor, removedColor) = getCodeDiffColors(baseColor, isHighContrast) - - val textColor = toCssHex(JBUI.CurrentTheme.Label.foreground()) - val linkColor = toCssHex(JBUI.CurrentTheme.Link.Foreground.ENABLED) - val dataFlowColor = toCssHex(baseColor) - val borderColor = toCssHex(JBUI.CurrentTheme.CustomFrameDecorations.separatorForeground()) - val editorColor = - toCssHex(UIUtil.getTextFieldBackground()) - - val globalScheme = EditorColorsManager.getInstance().globalScheme - val tearLineColor = - globalScheme.getColor(ColorKey.find("TEARLINE_COLOR")) // The closest color to target_rgb = (198, 198, 200) - val tabItemHoverColor = - globalScheme.getColor(ColorKey.find("INDENT_GUIDE")) // The closest color to target_rgb = RGB (235, 236, 240) - - val editorColorsManager = EditorColorsManager.getInstance() - val editorColorsScheme: EditorColorsScheme = editorColorsManager.globalScheme - val fontPreferences: FontPreferences = editorColorsScheme.fontPreferences - val editorFont = fontPreferences.fontFamily - - val themeScript = """ - (function(){ - if (window.themeApplied) { - return; - } - window.themeApplied = true; - const style = getComputedStyle(document.documentElement); - const properties = { - '--text-color': "$textColor", - '--link-color': "$linkColor", - '--data-flow-body-color': "$dataFlowColor", - '--example-line-added-color': "${toCssHex(addedColor)}", - '--example-line-removed-color': "${toCssHex(removedColor)}", - '--tab-item-github-icon-color': "$textColor", - '--tab-item-hover-color': "${tabItemHoverColor?.let { toCssHex(it) }}", - '--scrollbar-thumb-color': "${tearLineColor?.let { toCssHex(it) }}", - '--tabs-bottom-color': "${tearLineColor?.let { toCssHex(it) }}", - '--border-color': "$borderColor", - '--editor-color': "$editorColor", - '--editor-font': "'$editorFont'", - }; - for (let [property, value] of Object.entries(properties)) { - document.documentElement.style.setProperty(property, value); - } - - // Add theme class to body - const isDarkTheme = $isDarkTheme; - const isHighContrast = $isHighContrast; - document.body.classList.add(isHighContrast ? 'high-contrast' : (isDarkTheme ? 'dark' : 'light')); - })(); - """ - browser.executeJavaScript(themeScript, browser.url, 0) } } } diff --git a/src/main/kotlin/io/snyk/plugin/ui/jcef/ThemeBasedStylingGenerator.kt b/src/main/kotlin/io/snyk/plugin/ui/jcef/ThemeBasedStylingGenerator.kt new file mode 100644 index 000000000..7b1b7b78d --- /dev/null +++ b/src/main/kotlin/io/snyk/plugin/ui/jcef/ThemeBasedStylingGenerator.kt @@ -0,0 +1,119 @@ +package io.snyk.plugin.ui.jcef + +import com.intellij.openapi.editor.colors.ColorKey +import com.intellij.openapi.editor.colors.EditorColorsManager +import com.intellij.openapi.editor.colors.EditorColorsScheme +import com.intellij.openapi.editor.colors.FontPreferences +import com.intellij.ui.jcef.JBCefBrowserBase +import com.intellij.util.ui.JBUI +import com.intellij.util.ui.UIUtil +import org.cef.browser.CefBrowser +import org.cef.browser.CefFrame +import org.cef.handler.CefLoadHandlerAdapter +import java.awt.Color + +class ThemeBasedStylingGenerator { + fun toCssHex(color: Color): String { + return "#%02x%02x%02x".format(color.red, color.green, color.blue) + } + + fun shift( + colorComponent: Int, + d: Double, + ): Int { + val n = (colorComponent * d).toInt() + return n.coerceIn(0, 255) + } + + fun getCodeDiffColors( + baseColor: Color, + isHighContrast: Boolean, + ): Pair { + val addedColor = + if (isHighContrast) { + Color(28, 68, 40) // high contrast green + } else { + Color(shift(baseColor.red, 0.75), baseColor.green, shift(baseColor.blue, 0.75)) + } + + val removedColor = + if (isHighContrast) { + Color(84, 36, 38) // high contrast red + } else { + Color(shift(baseColor.red, 1.25), shift(baseColor.green, 0.85), shift(baseColor.blue, 0.85)) + } + return Pair(addedColor, removedColor) + } + + fun generate(jbCefBrowser: JBCefBrowserBase): CefLoadHandlerAdapter { + val isDarkTheme = EditorColorsManager.getInstance().isDarkEditor + val isHighContrast = + EditorColorsManager.getInstance().globalScheme.name.contains("High contrast", ignoreCase = true) + + return object : CefLoadHandlerAdapter() { + override fun onLoadEnd( + browser: CefBrowser, + frame: CefFrame, + httpStatusCode: Int, + ) { + if (frame.isMain) { + val baseColor = UIUtil.getTextFieldBackground() + val (addedColor, removedColor) = getCodeDiffColors(baseColor, isHighContrast) + + val textColor = toCssHex(JBUI.CurrentTheme.Label.foreground()) + val linkColor = toCssHex(JBUI.CurrentTheme.Link.Foreground.ENABLED) + val dataFlowColor = toCssHex(baseColor) + val borderColor = toCssHex(JBUI.CurrentTheme.CustomFrameDecorations.separatorForeground()) + val editorColor = + toCssHex(UIUtil.getTextFieldBackground()) + val labelColor = toCssHex(JBUI.CurrentTheme.Label.foreground()) + + val globalScheme = EditorColorsManager.getInstance().globalScheme + val tearLineColor = + globalScheme.getColor(ColorKey.find("TEARLINE_COLOR")) // The closest color to target_rgb = (198, 198, 200) + val tabItemHoverColor = + globalScheme.getColor(ColorKey.find("INDENT_GUIDE")) // The closest color to target_rgb = RGB (235, 236, 240) + + val editorColorsManager = EditorColorsManager.getInstance() + val editorColorsScheme: EditorColorsScheme = editorColorsManager.globalScheme + val fontPreferences: FontPreferences = editorColorsScheme.fontPreferences + val editorFont = fontPreferences.fontFamily + + val themeScript = """ + (function(){ + if (window.themeApplied) { + return; + } + window.themeApplied = true; + const style = getComputedStyle(document.documentElement); + const properties = { + '--text-color': "$textColor", + '--link-color': "$linkColor", + '--data-flow-body-color': "$dataFlowColor", + '--example-line-added-color': "${toCssHex(addedColor)}", + '--example-line-removed-color': "${toCssHex(removedColor)}", + '--tab-item-github-icon-color': "$textColor", + '--tab-item-hover-color': "${tabItemHoverColor?.let { toCssHex(it) }}", + '--scrollbar-thumb-color': "${tearLineColor?.let { toCssHex(it) }}", + '--tabs-bottom-color': "${tearLineColor?.let { toCssHex(it) }}", + '--border-color': "$borderColor", + '--editor-color': "$editorColor", + '--editor-font': "'$editorFont'", + '--label-color': "'$labelColor'", + }; + for (let [property, value] of Object.entries(properties)) { + document.documentElement.style.setProperty(property, value); + } + + // Add theme class to body + const isDarkTheme = $isDarkTheme; + const isHighContrast = $isHighContrast; + document.body.classList.add(isHighContrast ? 'high-contrast' : (isDarkTheme ? 'dark' : 'light')); + })(); + """ + browser.executeJavaScript(themeScript, browser.url, 0) + } + } + } + } +} diff --git a/src/main/kotlin/io/snyk/plugin/ui/jcef/Utils.kt b/src/main/kotlin/io/snyk/plugin/ui/jcef/Utils.kt index 6a3313fca..d5998e20d 100644 --- a/src/main/kotlin/io/snyk/plugin/ui/jcef/Utils.kt +++ b/src/main/kotlin/io/snyk/plugin/ui/jcef/Utils.kt @@ -6,10 +6,12 @@ import com.intellij.ui.jcef.JBCefBrowserBuilder import org.cef.handler.CefLoadHandlerAdapter import java.awt.Component +typealias LoadHandlerGenerator = (jbCefBrowser: JBCefBrowser) -> CefLoadHandlerAdapter + object JCEFUtils { fun getJBCefBrowserComponentIfSupported( html: String, - loadHandlerGenerator: (jbCefBrowser: JBCefBrowser) -> CefLoadHandlerAdapter, + loadHandlerGenerators: List, ): Component? { if (!JBCefApp.isSupported()) { return null @@ -21,9 +23,10 @@ object JCEFUtils { .setMouseWheelEventEnable(true).build() jbCefBrowser.setOpenLinksInExternalBrowser(true) - val loadHandler = loadHandlerGenerator(jbCefBrowser) - cefClient.addLoadHandler(loadHandler, jbCefBrowser.cefBrowser) - + for (loadHandlerGenerator in loadHandlerGenerators) { + val loadHandler = loadHandlerGenerator(jbCefBrowser) + cefClient.addLoadHandler(loadHandler, jbCefBrowser.cefBrowser) + } jbCefBrowser.loadHTML(html, jbCefBrowser.cefBrowser.url) return jbCefBrowser.component diff --git a/src/main/kotlin/io/snyk/plugin/ui/toolwindow/panels/JCEFDescriptionPanel.kt b/src/main/kotlin/io/snyk/plugin/ui/toolwindow/panels/JCEFDescriptionPanel.kt index c1de8d3c3..02bd0bf62 100644 --- a/src/main/kotlin/io/snyk/plugin/ui/toolwindow/panels/JCEFDescriptionPanel.kt +++ b/src/main/kotlin/io/snyk/plugin/ui/toolwindow/panels/JCEFDescriptionPanel.kt @@ -5,13 +5,14 @@ import com.intellij.openapi.vfs.VirtualFileManager import com.intellij.uiDesigner.core.GridLayoutManager import com.intellij.util.ui.JBUI import io.snyk.plugin.SnykFile -import io.snyk.plugin.pluginSettings import io.snyk.plugin.ui.DescriptionHeaderPanel import io.snyk.plugin.ui.SnykBalloonNotificationHelper import io.snyk.plugin.ui.baseGridConstraintsAnchorWest import io.snyk.plugin.ui.descriptionHeaderPanel import io.snyk.plugin.ui.jcef.JCEFUtils +import io.snyk.plugin.ui.jcef.LoadHandlerGenerator import io.snyk.plugin.ui.jcef.OpenFileLoadHandlerGenerator +import io.snyk.plugin.ui.jcef.ThemeBasedStylingGenerator import io.snyk.plugin.ui.panelGridConstraints import io.snyk.plugin.ui.toolwindow.SnykToolWindowPanel import io.snyk.plugin.ui.wrapWithScrollPane @@ -23,6 +24,7 @@ import java.awt.Font import java.nio.file.Paths import javax.swing.JLabel import javax.swing.JPanel +import kotlin.collections.set class SuggestionDescriptionPanelFromLS( snykFile: SnykFile, @@ -36,22 +38,31 @@ class SuggestionDescriptionPanelFromLS( "Snyk encountered an issue while rendering the vulnerability description. Please try again, or contact support if the problem persists. We apologize for any inconvenience caused." init { - if ( - pluginSettings().isGlobalIgnoresFeatureEnabled && - issue.canLoadSuggestionPanelFromHTML() - ) { - val virtualFiles = LinkedHashMap() - for (dataFlow in issue.additionalData.dataFlow) { - virtualFiles[dataFlow.filePath] = - VirtualFileManager.getInstance().findFileByNioPath(Paths.get(dataFlow.filePath)) + if (issue.canLoadSuggestionPanelFromHTML()) { + val loadHandlerGenerators: MutableList = + emptyList().toMutableList() + + loadHandlerGenerators += { + ThemeBasedStylingGenerator().generate(it) } - val openFileLoadHandlerGenerator = OpenFileLoadHandlerGenerator(snykFile.project, virtualFiles) - val html = this.getStyledHTML() - val jbCefBrowserComponent = - JCEFUtils.getJBCefBrowserComponentIfSupported(html) { + if (issue.additionalData.getProductType() == ProductType.CODE_SECURITY || + issue.additionalData.getProductType() == ProductType.CODE_QUALITY + ) { + val virtualFiles = LinkedHashMap() + for (dataFlow in issue.additionalData.dataFlow) { + virtualFiles[dataFlow.filePath] = + VirtualFileManager.getInstance().findFileByNioPath(Paths.get(dataFlow.filePath)) + } + + val openFileLoadHandlerGenerator = OpenFileLoadHandlerGenerator(snykFile.project, virtualFiles) + loadHandlerGenerators += { openFileLoadHandlerGenerator.generate(it) } + } + val html = this.getStyledHTML() + val jbCefBrowserComponent = + JCEFUtils.getJBCefBrowserComponentIfSupported(html, loadHandlerGenerators) if (jbCefBrowserComponent == null) { val statePanel = StatePanel(SnykToolWindowPanel.SELECT_ISSUE_TEXT) this.add(wrapWithScrollPane(statePanel), BorderLayout.CENTER) @@ -94,7 +105,9 @@ class SuggestionDescriptionPanelFromLS( JPanel( GridLayoutManager(lastRowToAddSpacer + 1, 1, JBUI.insets(0, 10, 20, 10), -1, 20), ).apply { - if (issue.additionalData.getProductType() == ProductType.CODE_SECURITY || issue.additionalData.getProductType() == ProductType.CODE_QUALITY) { + if (issue.additionalData.getProductType() == ProductType.CODE_SECURITY || + issue.additionalData.getProductType() == ProductType.CODE_QUALITY + ) { this.add( SnykCodeOverviewPanel(issue.additionalData), panelGridConstraints(2), @@ -134,8 +147,11 @@ class SuggestionDescriptionPanelFromLS( issue.additionalData.getProductType() == ProductType.CODE_QUALITY ) { ideStyle = SnykStylesheets.SnykCodeSuggestion + } else if (issue.additionalData.getProductType() == ProductType.OSS) { + ideStyle = SnykStylesheets.SnykOSSSuggestion } html = html.replace("\${ideStyle}", "") + html = html.replace("\${headerEnd}", "") html = html.replace( "\${ideScript}", diff --git a/src/main/kotlin/snyk/common/lsp/Types.kt b/src/main/kotlin/snyk/common/lsp/Types.kt index 41152e143..cd9958866 100644 --- a/src/main/kotlin/snyk/common/lsp/Types.kt +++ b/src/main/kotlin/snyk/common/lsp/Types.kt @@ -261,8 +261,10 @@ data class ScanIssue( fun canLoadSuggestionPanelFromHTML(): Boolean { return when (this.additionalData.getProductType()) { - ProductType.OSS -> false - ProductType.CODE_SECURITY, ProductType.CODE_QUALITY -> this.additionalData.details != null + ProductType.OSS -> true + ProductType.CODE_SECURITY, ProductType.CODE_QUALITY -> + pluginSettings().isGlobalIgnoresFeatureEnabled && this.additionalData.details != null + else -> TODO() } } diff --git a/src/main/kotlin/stylesheets/SnykStylesheets.kt b/src/main/kotlin/stylesheets/SnykStylesheets.kt index 9ae59525c..2cd2dc6aa 100644 --- a/src/main/kotlin/stylesheets/SnykStylesheets.kt +++ b/src/main/kotlin/stylesheets/SnykStylesheets.kt @@ -3,9 +3,9 @@ package stylesheets object SnykStylesheets { private fun getStylesheet(name: String): String { return this::class.java.getResource(name)?.readText() - ?: "" + ?: "" } val SnykCodeSuggestion = getStylesheet("/stylesheets/snyk_code_suggestion.css") + val SnykOSSSuggestion = getStylesheet("/stylesheets/snyk_oss_suggestion.css") } - diff --git a/src/main/resources/stylesheets/snyk_oss_suggestion.scss b/src/main/resources/stylesheets/snyk_oss_suggestion.scss new file mode 100644 index 000000000..3a858fdbe --- /dev/null +++ b/src/main/resources/stylesheets/snyk_oss_suggestion.scss @@ -0,0 +1,78 @@ +::-webkit-scrollbar-thumb { + background: var(--scrollbar-thumb-color); +} + +::-webkit-scrollbar-thumb:hover { + background: #595a5c; +} + +html, body { + height: 100%; + font-size: 16px; + display: flex; + flex-direction: column; + margin: 0; + padding: 0; +} + +body { + color: var(--text-color); + font-weight: 400; + font-size: 0.875rem; +} + +.font-light { + font-weight: bold; +} + +a, +.link { + color: var(--link-color); +} + +.delimiter { + border-right: 1px solid var(--border-color); +} + +.suggestion--header { + padding-top: 10px; +} + +.suggestion .suggestion-text { + font-size: 1.5rem; + position: relative; + top: -5%; +} + +.summary .summary-item { + margin-bottom: 0.8em; +} + +.summary .label { + font-size: 0.8rem; +} + +.suggestion--header > h2, +.summary > h2, +.vulnerability-overview > h2 { + font-size: 0.9rem; + margin-bottom: 1.5em; +} + +.identifiers { + padding-bottom: 20px; +} + +.vulnerability-overview pre { + font-size: 1.05rem; + font-family: var(--editor-font); + background-color: transparent; + border: 1px solid transparent; + padding: 0; +} + +.vulnerability-overview code { + background-color: transparent; + font-size: 1.05rem; + font-family: var(--editor-font); +} diff --git a/src/test/kotlin/io/snyk/plugin/ui/toolwindow/SuggestionDescriptionPanelFromLSCodeTest.kt b/src/test/kotlin/io/snyk/plugin/ui/toolwindow/SuggestionDescriptionPanelFromLSCodeTest.kt index f766540cc..a717a650b 100644 --- a/src/test/kotlin/io/snyk/plugin/ui/toolwindow/SuggestionDescriptionPanelFromLSCodeTest.kt +++ b/src/test/kotlin/io/snyk/plugin/ui/toolwindow/SuggestionDescriptionPanelFromLSCodeTest.kt @@ -12,7 +12,6 @@ import io.mockk.mockkObject import io.mockk.unmockkAll import io.snyk.plugin.Severity import io.snyk.plugin.SnykFile -import io.snyk.plugin.pluginSettings import io.snyk.plugin.resetSettings import io.snyk.plugin.ui.jcef.JCEFUtils import io.snyk.plugin.ui.toolwindow.panels.SuggestionDescriptionPanelFromLS @@ -82,7 +81,9 @@ class SuggestionDescriptionPanelFromLSCodeTest : BasePlatformTestCase() { } @Test - fun `test createUI should build the right panels for Snyk Code`() { + fun `test createUI should build the right panels for Snyk Code if HTML is not allowed`() { + every { issue.canLoadSuggestionPanelFromHTML() } returns false + cut = SuggestionDescriptionPanelFromLS(snykFile, issue) val issueNaming = getJLabelByText(cut, issue.issueNaming()) @@ -108,22 +109,9 @@ class SuggestionDescriptionPanelFromLSCodeTest : BasePlatformTestCase() { } @Test - fun `test createUI should build panel with issue message as overview label if the feature flag is not enabled`() { - every { issue.details() } returns "" - cut = SuggestionDescriptionPanelFromLS(snykFile, issue) - - val actual = getJLabelByText(cut, "Test message") - assertNotNull(actual) - - val actualBrowser = getJBCEFBrowser(cut) - assertNull(actualBrowser) - } - - @Test - fun `test createUI should build panel with issue message as overview label if HTML is not allowed, even if the feature flag is enabled`() { - pluginSettings().isGlobalIgnoresFeatureEnabled = true - + fun `test createUI should build panel with issue message as overview label if HTML is not allowed`() { every { issue.canLoadSuggestionPanelFromHTML() } returns false + cut = SuggestionDescriptionPanelFromLS(snykFile, issue) val actual = getJLabelByText(cut, "Test message") @@ -134,9 +122,7 @@ class SuggestionDescriptionPanelFromLSCodeTest : BasePlatformTestCase() { } @Test - fun `test createUI should show nothing if feature flag is enabled but JCEF is not`() { - pluginSettings().isGlobalIgnoresFeatureEnabled = true - + fun `test createUI should show nothing if HTML is allowed but JCEF is not supported`() { mockkObject(JCEFUtils) every { JCEFUtils.getJBCefBrowserComponentIfSupported(eq("HTML message"), any()) } returns null @@ -152,9 +138,7 @@ class SuggestionDescriptionPanelFromLSCodeTest : BasePlatformTestCase() { } @Test - fun `test createUI should build panel with HTML from details if feature flag is enabled`() { - pluginSettings().isGlobalIgnoresFeatureEnabled = true - + fun `test createUI should build panel with HTML from details if allowed`() { val mockJBCefBrowserComponent = JLabel("HTML message") mockkObject(JCEFUtils) every { @@ -173,9 +157,7 @@ class SuggestionDescriptionPanelFromLSCodeTest : BasePlatformTestCase() { } @Test - fun `test getStyledHTML should inject CSS into the HTML`() { - pluginSettings().isGlobalIgnoresFeatureEnabled = true - + fun `test getStyledHTML should inject CSS into the HTML if allowed`() { every { issue.details() } returns "\${ideStyle}HTML message" every { issue.canLoadSuggestionPanelFromHTML() } returns true cut = SuggestionDescriptionPanelFromLS(snykFile, issue) diff --git a/src/test/kotlin/io/snyk/plugin/ui/toolwindow/SuggestionDescriptionPanelFromLSOSSTest.kt b/src/test/kotlin/io/snyk/plugin/ui/toolwindow/SuggestionDescriptionPanelFromLSOSSTest.kt index 3dc28eb62..418009594 100644 --- a/src/test/kotlin/io/snyk/plugin/ui/toolwindow/SuggestionDescriptionPanelFromLSOSSTest.kt +++ b/src/test/kotlin/io/snyk/plugin/ui/toolwindow/SuggestionDescriptionPanelFromLSOSSTest.kt @@ -8,11 +8,12 @@ import com.intellij.psi.PsiFile import com.intellij.testFramework.fixtures.BasePlatformTestCase import io.mockk.every import io.mockk.mockk +import io.mockk.mockkObject import io.mockk.unmockkAll import io.snyk.plugin.Severity import io.snyk.plugin.SnykFile -import io.snyk.plugin.pluginSettings import io.snyk.plugin.resetSettings +import io.snyk.plugin.ui.jcef.JCEFUtils import io.snyk.plugin.ui.toolwindow.panels.SuggestionDescriptionPanelFromLS import org.junit.Test import snyk.UIComponentFinder.getActionLinkByText @@ -23,6 +24,7 @@ import snyk.common.ProductType import snyk.common.lsp.IssueData import snyk.common.lsp.ScanIssue import java.nio.file.Paths +import javax.swing.JLabel class SuggestionDescriptionPanelFromLSOSSTest : BasePlatformTestCase() { private lateinit var cut: SuggestionDescriptionPanelFromLS @@ -78,7 +80,9 @@ class SuggestionDescriptionPanelFromLSOSSTest : BasePlatformTestCase() { } @Test - fun `test createUI should build the right panels for Snyk OSS`() { + fun `test createUI should build the right panels for Snyk OSS if HTML not allowed`() { + every { issue.canLoadSuggestionPanelFromHTML() } returns false + cut = SuggestionDescriptionPanelFromLS(snykFile, issue) val issueNaming = getJLabelByText(cut, issue.issueNaming()) @@ -110,9 +114,26 @@ class SuggestionDescriptionPanelFromLSOSSTest : BasePlatformTestCase() { } @Test - fun `test getStyledHTML should inject CSS into the HTML`() { - pluginSettings().isGlobalIgnoresFeatureEnabled = true + fun `test createUI should build panel with HTML from details if allowed`() { + val mockJBCefBrowserComponent = JLabel("HTML message") + mockkObject(JCEFUtils) + every { + JCEFUtils.getJBCefBrowserComponentIfSupported(eq("HTML message"), any()) + } returns mockJBCefBrowserComponent + + every { issue.details() } returns "HTML message" + every { issue.canLoadSuggestionPanelFromHTML() } returns true + cut = SuggestionDescriptionPanelFromLS(snykFile, issue) + val actual = getJLabelByText(cut, "Test message") + assertNull(actual) + + val actualBrowser = getJLabelByText(cut, "HTML message") + assertNotNull(actualBrowser) + } + + @Test + fun `test getStyledHTML should inject CSS into the HTML if allowed`() { every { issue.details() } returns "HTML message" every { issue.canLoadSuggestionPanelFromHTML() } returns true cut = SuggestionDescriptionPanelFromLS(snykFile, issue)