@@ -34,10 +34,10 @@ import javax.swing.Icon
3434 * import. We reconstruct the same import, but build the console on a subclass that re-delegates `createImportActions`,
3535 * so an imported-history tab carries the exact toolbar a live run does.
3636 */
37- internal fun openTestoHistory (project : Project , file : VirtualFile ) {
37+ internal fun openTestoHistory (project : Project , file : VirtualFile , targetUrl : String? = null ) {
3838 try {
3939 val executor = DefaultRunExecutor .getRunExecutorInstance()
40- val profile = TestoImportRunProfile (file, project, executor)
40+ val profile = TestoImportRunProfile (file, project, executor, targetUrl )
4141 ExecutionEnvironmentBuilder .create(project, executor, profile)
4242 .executor(executor)
4343 .target(profile.target)
@@ -48,6 +48,29 @@ internal fun openTestoHistory(project: Project, file: VirtualFile) {
4848 }
4949}
5050
51+ /* *
52+ * "Show history" for one test: open the most recent saved run that actually contains [url] (so clicking a test doesn't
53+ * land on an unrelated latest run), and once imported, select that test's node. Falls back to the newest run overall.
54+ * Scans files off the EDT (the largest history XML is sizeable), then imports on the EDT.
55+ */
56+ internal fun openTestoHistoryForTest (project : Project , url : String ) {
57+ com.intellij.openapi.application.ApplicationManager .getApplication().executeOnPooledThread {
58+ val root = com.intellij.execution.TestStateStorage .getTestHistoryRoot(project)
59+ val files = com.intellij.execution.testframework.sm.TestHistoryConfiguration .getInstance(project).files
60+ .map { File (root, it) }
61+ .filter { it.exists() }
62+ .sortedByDescending { it.lastModified() }
63+ val target = files.firstOrNull { f -> runCatching { f.readText().contains(url) }.getOrDefault(false ) }
64+ ? : files.firstOrNull()
65+ ? : return @executeOnPooledThread
66+ com.intellij.openapi.application.ApplicationManager .getApplication().invokeLater {
67+ val vf = com.intellij.openapi.vfs.LocalFileSystem .getInstance().refreshAndFindFileByIoFile(target)
68+ ? : return @invokeLater
69+ openTestoHistory(project, vf, url)
70+ }
71+ }
72+ }
73+
5174/* *
5275 * Mirrors `AbstractImportTestsAction.ImportRunProfile` (reused for parsing the saved `<config>` and resolving the
5376 * target), but its first [getState] returns our [TestoImportedRunnableState] so the console is built on our properties.
@@ -58,6 +81,7 @@ internal class TestoImportRunProfile(
5881 file : VirtualFile ,
5982 project : Project ,
6083 private val executor : Executor ,
84+ private val targetUrl : String? = null ,
6185) : RunProfile {
6286 private val inner = AbstractImportTestsAction .ImportRunProfile (file, project, executor)
6387 private val ioFile = VfsUtilCore .virtualToIoFile(file)
@@ -73,7 +97,7 @@ internal class TestoImportRunProfile(
7397 val config = inner.initialConfiguration
7498 if (! imported && config is SMRunnerConsolePropertiesProvider ) {
7599 imported = true
76- return TestoImportedRunnableState (config as RunConfiguration , ioFile)
100+ return TestoImportedRunnableState (config as RunConfiguration , ioFile, targetUrl )
77101 }
78102 // Re-run from an imported tab (second invocation): run the original configuration's tests, not a re-import.
79103 return config?.getState(executor, environment) ? : inner.getState(executor, environment)
@@ -87,13 +111,14 @@ internal class TestoImportRunProfile(
87111private class TestoImportedRunnableState (
88112 private val initialConfig : RunConfiguration ,
89113 private val file : File ,
114+ private val targetUrl : String? ,
90115) : RunProfileState {
91116 override fun execute (executor : Executor , runner : ProgramRunner <* >): ExecutionResult {
92117 val handler = NopProcessHandler ()
93118 val delegate = (initialConfig as SMRunnerConsolePropertiesProvider )
94119 .createTestConsoleProperties(DefaultRunExecutor .getRunExecutorInstance()) as TestoConsoleProperties
95120 val props = TestoImportedConsoleProperties (
96- delegate, file, handler, initialConfig.project, initialConfig, delegate.testFrameworkName, executor,
121+ delegate, file, handler, initialConfig.project, initialConfig, delegate.testFrameworkName, executor, targetUrl,
97122 )
98123 Disposer .register(props, delegate)
99124
@@ -120,6 +145,8 @@ internal class TestoImportedConsoleProperties(
120145 config : RunConfiguration ,
121146 frameworkName : String ,
122147 executor : Executor ,
148+ // The locationUrl of the test the "Show history" lens was clicked on, so installForImport can select its node.
149+ val targetUrl : String? = null ,
123150) : ImportedTestConsoleProperties(delegate, file, handler, project, config, frameworkName, executor) {
124151 override fun createImportActions (): Array <AnAction > = delegate.createImportActions()
125152}
0 commit comments