diff --git a/.run/Run Plugin Tests.run.xml b/.run/Run Plugin Tests.run.xml deleted file mode 100644 index ae9ae135..00000000 --- a/.run/Run Plugin Tests.run.xml +++ /dev/null @@ -1,24 +0,0 @@ - - - - - - - - true - true - false - - - diff --git a/src/main/kotlin/com/dsoftware/ghmanager/ui/GhActionsToolWindowFactory.kt b/src/main/kotlin/com/dsoftware/ghmanager/ui/GhActionsToolWindowFactory.kt index 72dd473c..8542e226 100644 --- a/src/main/kotlin/com/dsoftware/ghmanager/ui/GhActionsToolWindowFactory.kt +++ b/src/main/kotlin/com/dsoftware/ghmanager/ui/GhActionsToolWindowFactory.kt @@ -43,7 +43,6 @@ internal class ProjectRepositories(val toolWindow: ToolWindow) { class GhActionsToolWindowFactory : ToolWindowFactory, DumbAware { private lateinit var settingsService: GhActionsSettingsService - private val accountManager = service() private val projectReposMap = mutableMapOf() private val scope = CoroutineScope(SupervisorJob()) @@ -68,6 +67,7 @@ class GhActionsToolWindowFactory : ToolWindowFactory, DumbAware { updateRepos(toolWindow, it) } } + val accountManager = service() scope.launch { accountManager.accountsState.collect { updateRepos(toolWindow, repositoriesManager.knownRepositories) @@ -104,23 +104,23 @@ class GhActionsToolWindowFactory : ToolWindowFactory, DumbAware { if ((GHAccountsUtil.accounts.isEmpty() && settingsService.state.useGitHubSettings) || (!settingsService.state.useGitHubSettings && settingsService.state.apiToken == "") ) { - noGitHubAccountPanel(disposable, projectRepos) + createNoAccountPanel(disposable, projectRepos) } else if (projectRepos.knownRepositories.isEmpty()) { - noRepositories(disposable, projectRepos) + createNoReposPanel(disposable, projectRepos) } else { val countRepos = projectRepos.knownRepositories.count { settingsService.state.customRepos[it.remote.url]?.included ?: false } if (settingsService.state.useCustomRepos && countRepos == 0) { - noActiveRepositories(disposable, projectRepos) + createNoActiveReposPanel(disposable, projectRepos) } else { - ghAccountAndReposConfigured(disposable, projectRepos) + createRepoWorkflowsPanels(disposable, projectRepos) } } } } - private fun noActiveRepositories( + private fun createNoActiveReposPanel( disposable: Disposable, projectRepositories: ProjectRepositories ) = @@ -148,7 +148,7 @@ class GhActionsToolWindowFactory : ToolWindowFactory, DumbAware { ) } - private fun noGitHubAccountPanel( + private fun createNoAccountPanel( disposable: Disposable, projectRepositories: ProjectRepositories ) = with(projectRepositories.toolWindow.contentManager) { @@ -182,7 +182,7 @@ class GhActionsToolWindowFactory : ToolWindowFactory, DumbAware { ) } - private fun noRepositories( + private fun createNoReposPanel( disposable: Disposable, projectRepositories: ProjectRepositories ) = with(projectRepositories.toolWindow.contentManager) { @@ -203,7 +203,7 @@ class GhActionsToolWindowFactory : ToolWindowFactory, DumbAware { return accounts.firstOrNull { it.server.equals(repo.repository.serverPath, true) } } - private fun ghAccountAndReposConfigured( + private fun createRepoWorkflowsPanels( parentDisposable: Disposable, projectRepositories: ProjectRepositories ) = diff --git a/src/test/kotlin/com/dsoftware/ghmanager/GitHubActionsManagerBaseTest.kt b/src/test/kotlin/com/dsoftware/ghmanager/GitHubActionsManagerBaseTest.kt new file mode 100644 index 00000000..a52c606b --- /dev/null +++ b/src/test/kotlin/com/dsoftware/ghmanager/GitHubActionsManagerBaseTest.kt @@ -0,0 +1,151 @@ +package com.dsoftware.ghmanager + +import com.dsoftware.ghmanager.ui.GhActionsToolWindowFactory +import com.intellij.openapi.components.service +import com.intellij.openapi.diagnostic.Logger +import com.intellij.openapi.project.Project +import com.intellij.openapi.wm.ToolWindow +import com.intellij.platform.ide.progress.runWithModalProgressBlocking +import com.intellij.testFramework.PlatformTestUtil +import com.intellij.testFramework.RunAll +import com.intellij.testFramework.fixtures.BasePlatformTestCase +import com.intellij.toolWindow.ToolWindowHeadlessManagerImpl.MockToolWindow +import com.intellij.util.ThrowableRunnable +import com.intellij.util.concurrency.annotations.RequiresEdt +import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.yield +import org.jetbrains.plugins.github.api.GithubApiRequestExecutor +import org.jetbrains.plugins.github.api.GithubApiRequests +import org.jetbrains.plugins.github.api.GithubServerPath +import org.jetbrains.plugins.github.api.data.GithubRepo +import org.jetbrains.plugins.github.api.data.GithubResponsePage +import org.jetbrains.plugins.github.authentication.GHAccountsUtil +import org.jetbrains.plugins.github.authentication.accounts.GHAccountManager +import org.jetbrains.plugins.github.authentication.accounts.GithubAccount +import org.jetbrains.plugins.github.util.GHHostedRepositoriesManager + + +/** + * + * The base class for JUnit platform tests of the github plugin.

+ * Extend this test to write a test on GitHub which has the following features/limitations: + * + * * This is a "platform test case", which means that IDEA "almost" production platform is set up before the test starts. + * * Project base directory is the root of everything. + * + * + * All tests inherited from this class are required to have a token to access the Github server. + * They are set up in Environment variables:

+ * `idea_test_github_host

+ * idea_test_github_token1

// token test user + * idea_test_github_token2` // token user with configured test repositories + * + */ +abstract class GitHubActionsManagerBaseTest : BasePlatformTestCase() { + private lateinit var accountManager: GHAccountManager + private lateinit var organisation: String + protected lateinit var repositoriesManager: GHHostedRepositoriesManager + protected lateinit var mainAccount: AccountData + + private val mainRepos = mutableSetOf() + protected lateinit var myProject: Project + protected lateinit var factory: GhActionsToolWindowFactory + protected lateinit var toolWindow: ToolWindow + override fun setUp() { + super.setUp() + myProject = project + factory = GhActionsToolWindowFactory() + toolWindow = MockToolWindow(myProject) + val host = + GithubServerPath.from(System.getenv("idea_test_github_host") ?: System.getenv("idea.test.github.host")) + val token1 = System.getenv("idea_test_github_token1") ?: System.getenv("idea.test.github.token1") + + assertNotNull(token1) + accountManager = service() + repositoriesManager = project.service() + + organisation = System.getenv("idea_test_github_org") ?: System.getenv("idea.test.github.org") + assertNotNull(organisation) + mainAccount = createAccountData(host, token1) + setCurrentAccount(mainAccount) + } + + private fun createAccountData(host: GithubServerPath, token: String): AccountData { + val account = GHAccountManager.createAccount("token", host) + runBlocking { accountManager.updateAccount(account, token) } + val executor = service().create(token) + val username = executor.execute(GithubApiRequests.CurrentUser.get(account.server)).login + val repos = + executor.execute>(GithubApiRequests.CurrentUser.Repos.get(account.server)) + repos.items.forEach { mainRepos.add(it.name) } + + return AccountData(token, account, username, executor) + } + + + protected open fun setCurrentAccount(accountData: AccountData?) { + GHAccountsUtil.setDefaultAccount(myProject, accountData?.account) + } + + protected data class AccountData( + val token: String, + val account: GithubAccount, + val username: String, + val executor: GithubApiRequestExecutor + ) + + + @Throws(Exception::class) + override fun tearDown() { + RunAll( + ThrowableRunnable { setCurrentAccount(null) }, + ThrowableRunnable { if (::accountManager.isInitialized) runBlocking { accountManager.updateAccounts(emptyMap()) } }, + ThrowableRunnable { super.tearDown() } + ).run() + } + +// +// private fun deleteRepos(account: AccountData, repos: Collection) { +// setCurrentAccount(account) +// for (repo in repos) { +// retry(LOG, true) { +// account.executor.execute(GithubApiRequests.Repos.delete(repo)) +// val info = account.executor.execute(GithubApiRequests.Repos.get(repo)) +// check(info == null) { "Repository still exists" } +// } +// } +// } + + companion object { + private const val RETRIES = 3 + + internal fun retry(LOG: Logger, exception: Boolean = true, action: () -> Unit) { + for (i in 1..RETRIES) { + try { + LOG.debug("Attempt #$i") + return action() + } catch (e: Throwable) { + if (i == RETRIES) { + if (exception) throw e + else { + LOG.error(e) + return + } + } + Thread.sleep(1000L) + } + } + } + } +} + +@RequiresEdt +fun executeSomeCoroutineTasksAndDispatchAllInvocationEvents(project: Project) { + repeat(3) { + PlatformTestUtil.dispatchAllInvocationEventsInIdeEventQueue() + runWithModalProgressBlocking(project, "") { + yield() + } + PlatformTestUtil.dispatchAllInvocationEventsInIdeEventQueue() + } +} \ No newline at end of file diff --git a/src/test/kotlin/com/dsoftware/ghmanager/ToolWindowFactoryTest.kt b/src/test/kotlin/com/dsoftware/ghmanager/ToolWindowFactoryTest.kt new file mode 100644 index 00000000..74a52eeb --- /dev/null +++ b/src/test/kotlin/com/dsoftware/ghmanager/ToolWindowFactoryTest.kt @@ -0,0 +1,16 @@ +package com.dsoftware.ghmanager + +import com.intellij.ui.components.JBPanelWithEmptyText +import junit.framework.TestCase + + +class ToolWindowFactoryTest : GitHubActionsManagerBaseTest() { + fun testNoGitHubAccountPanel() { + factory.init(toolWindow) + executeSomeCoroutineTasksAndDispatchAllInvocationEvents(project) + + TestCase.assertEquals(1, toolWindow.contentManager.contentCount) + val panel = toolWindow.contentManager.contents[0].component + TestCase.assertTrue(panel is JBPanelWithEmptyText) + } +} \ No newline at end of file