(IJPL-163481) What's New: add some tests for startup check

GitOrigin-RevId: 797129ce1270520eec127450d159e463c46f9252
This commit is contained in:
Ivan Migalev
2024-10-03 20:55:24 +02:00
committed by intellij-monorepo-bot
parent 9454e64fbc
commit 7f2f77f47a
4 changed files with 100 additions and 18 deletions

View File

@@ -40,7 +40,8 @@
<orderEntry type="library" scope="PROVIDED" name="kotlinc.kotlinx-serialization-compiler-plugin" level="project" />
<orderEntry type="library" name="kotlinx-serialization-json" level="project" />
<orderEntry type="library" name="kotlinx-serialization-core" level="project" />
<orderEntry type="library" scope="TEST" name="JUnit4" level="project" />
<orderEntry type="module" module-name="intellij.platform.ui.jcef" />
<orderEntry type="module" module-name="intellij.platform.testFramework" scope="TEST" />
<orderEntry type="module" module-name="intellij.platform.testFramework.junit5" scope="TEST" />
</component>
</module>

View File

@@ -27,7 +27,7 @@ import java.net.HttpURLConnection
import java.net.URL
import java.nio.charset.StandardCharsets
internal sealed class WhatsNewContent {
internal abstract class WhatsNewContent {
companion object {
private val DataContext.project: Project?
get() = CommonDataKeys.PROJECT.getData(this)

View File

@@ -8,7 +8,6 @@ import com.intellij.internal.performanceTests.ProjectInitializationDiagnosticSer
import com.intellij.openapi.actionSystem.ActionManager
import com.intellij.openapi.client.ClientKind
import com.intellij.openapi.client.ClientSessionsManager
import com.intellij.openapi.components.service
import com.intellij.openapi.diagnostic.logger
import com.intellij.openapi.project.Project
import com.intellij.openapi.startup.ProjectActivity
@@ -18,32 +17,55 @@ import com.intellij.util.application
import kotlinx.coroutines.withContext
import java.util.concurrent.atomic.AtomicBoolean
internal class WhatsNewShowOnStartCheckService : ProjectActivity {
private val ourStarted = AtomicBoolean(false)
internal interface WhatsNewEnvironmentAccessor {
val isForceDisabled: Boolean
suspend fun getWhatsNewContent(): WhatsNewContent?
fun findAction(): WhatsNewAction?
suspend fun showWhatsNew(project: Project, action: WhatsNewAction)
}
private class WhatsNewEnvironmentAccessorImpl : WhatsNewEnvironmentAccessor {
private val isPlaybackMode = SystemProperties.getBooleanProperty("idea.is.playback", false)
override val isForceDisabled: Boolean
get() = application.isHeadlessEnvironment
|| application.isUnitTestMode
|| isPlaybackMode
// TODO: disable for remdev since lux is not ready to open what's new
|| AppMode.isRemoteDevHost()
// TODO: disable for UI tests since UI tests are not ready for What's new
|| Registry.`is`("expose.ui.hierarchy.url", false)
override suspend fun getWhatsNewContent() = WhatsNewContent.getWhatsNewContent()
override fun findAction() = ActionManager.getInstance().getAction("WhatsNewAction") as? WhatsNewAction
override suspend fun showWhatsNew(project: Project, action: WhatsNewAction) {
action.openWhatsNew(project)
}
}
internal class WhatsNewShowOnStartCheckService(private val environment: WhatsNewEnvironmentAccessor) : ProjectActivity {
@Suppress("unused") // used by the component container
constructor() : this(WhatsNewEnvironmentAccessorImpl())
private val wasStarted = AtomicBoolean(false)
override suspend fun execute(project: Project) {
if (ourStarted.getAndSet(true)) return
if (application.isHeadlessEnvironment
|| application.isUnitTestMode
|| isPlaybackMode
// TODO: disable for remdev since lux is not ready to open what's new
|| AppMode.isRemoteDevHost()
// TODO: disable for UI tests since UI tests are not ready for What's new
|| Registry.`is`("expose.ui.hierarchy.url", false)) return
if (wasStarted.getAndSet(true)) return
if (environment.isForceDisabled) return
logger.info("Checking whether to show the What's New page on startup.")
// a bit hacky workaround but now we don't have any tools to forward local startup activities to a controller
val clientId = ClientSessionsManager.getAppSessions(ClientKind.CONTROLLER).firstOrNull()?.clientId ?: ClientId.localId
withContext(clientId.asContextElement()) {
val content = WhatsNewContent.getWhatsNewContent()
val content = environment.getWhatsNewContent()
logger.info("Got What's New content: $content")
if (content != null) {
if (WhatsNewContentVersionChecker.isNeedToShowContent(content).also { logger.info("Should show What's New: $it") }) {
val whatsNewAction = service<ActionManager>().getAction("WhatsNewAction") as? WhatsNewAction
val whatsNewAction = environment.findAction()
if (whatsNewAction != null) {
val activityTracker = ProjectInitializationDiagnosticService.registerTracker(project, "OpenWhatsNewOnStart");
whatsNewAction.openWhatsNew(project)
val activityTracker = ProjectInitializationDiagnosticService.registerTracker(project, "OpenWhatsNewOnStart")
environment.showWhatsNew(project, whatsNewAction)
activityTracker.activityFinished()
}
}
@@ -52,4 +74,4 @@ internal class WhatsNewShowOnStartCheckService : ProjectActivity {
}
}
private val logger = logger<WhatsNewShowOnStartCheckService>()
private val logger = logger<WhatsNewShowOnStartCheckService>()

View File

@@ -0,0 +1,59 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.platform.whatsNew
import com.intellij.openapi.actionSystem.DataContext
import com.intellij.openapi.project.Project
import com.intellij.testFramework.junit5.TestApplication
import com.intellij.testFramework.junit5.fixture.projectFixture
import kotlinx.coroutines.runBlocking
import org.junit.jupiter.api.Assertions.assertFalse
import org.junit.jupiter.api.Assertions.assertTrue
import org.junit.jupiter.api.Test
import java.util.concurrent.atomic.AtomicBoolean
@TestApplication
class OnStartCheckServiceTest {
private val project = projectFixture()
private val mockContent = object : WhatsNewContent() {
override fun getRequest(dataContext: DataContext?) = error("Mock object, do not call")
override fun getActionWhiteList() = error("Mock object, do not call")
override fun getVersion() = error("Mock object, do not call")
override suspend fun isAvailable() = error("Mock object, do not call")
}
@Test
fun `WhatsNew should be disabled`() {
val (environment, isCalled) = mockAccessor(isDisabled = true)
val service = WhatsNewShowOnStartCheckService(environment)
runBlocking {
service.execute(project.get())
assertFalse(isCalled.get(), "What's New page should not be shown")
}
}
@Test
fun `WhatsNew should be shown`() {
val (environment, isCalled) = mockAccessor(isDisabled = false)
val service = WhatsNewShowOnStartCheckService(environment)
runBlocking {
service.execute(project.get())
assertTrue(isCalled.get(), "What's New page should be shown")
}
}
private fun mockAccessor(isDisabled: Boolean): Pair<WhatsNewEnvironmentAccessor, AtomicBoolean> {
val isCalled = AtomicBoolean(false)
val environment = object : WhatsNewEnvironmentAccessor {
override val isForceDisabled = isDisabled
override suspend fun getWhatsNewContent() = mockContent
override fun findAction() = WhatsNewAction()
override suspend fun showWhatsNew(project: Project, action: WhatsNewAction) {
isCalled.set(true)
}
}
return environment to isCalled
}
}