mirror of
https://github.com/chylex/IntelliJ-IdeaVim.git
synced 2025-06-02 13:34:07 +02:00
fixed Ide Fatal Errors
added UI tests reporting
This commit is contained in:
parent
4158bf1663
commit
a58ca80fc9
66
.github/workflows/runUiTests.yml
vendored
Normal file
66
.github/workflows/runUiTests.yml
vendored
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
name: Run UI Tests
|
||||||
|
on:
|
||||||
|
workflow_dispatch
|
||||||
|
jobs:
|
||||||
|
build-for-ui-test-mac-os:
|
||||||
|
runs-on: macos-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: Setup Java
|
||||||
|
uses: actions/setup-java@v2.1.0
|
||||||
|
with:
|
||||||
|
distribution: zulu
|
||||||
|
java-version: 11
|
||||||
|
- name: Build Plugin
|
||||||
|
run: gradle :buildPlugin
|
||||||
|
- name: Run Idea
|
||||||
|
run: |
|
||||||
|
mkdir -p build/reports
|
||||||
|
gradle :runIdeForUiTests > build/reports/idea.log &
|
||||||
|
- name: Wait for Idea started
|
||||||
|
uses: jtalk/url-health-check-action@1.5
|
||||||
|
with:
|
||||||
|
url: http://127.0.0.1:8082
|
||||||
|
max-attempts: 20
|
||||||
|
retry-delay: 10s
|
||||||
|
- name: Tests
|
||||||
|
run: gradle :testUi
|
||||||
|
- name: Save fails report
|
||||||
|
if: ${{ failure() }}
|
||||||
|
uses: actions/upload-artifact@v2
|
||||||
|
with:
|
||||||
|
name: ui-test-fails-report-mac
|
||||||
|
path: |
|
||||||
|
build/reports
|
||||||
|
# build-for-ui-test-linux:
|
||||||
|
# runs-on: ubuntu-latest
|
||||||
|
# steps:
|
||||||
|
# - uses: actions/checkout@v2
|
||||||
|
# - name: Setup Java
|
||||||
|
# uses: actions/setup-java@v2.1.0
|
||||||
|
# with:
|
||||||
|
# distribution: zulu
|
||||||
|
# java-version: 11
|
||||||
|
# - name: Build Plugin
|
||||||
|
# run: gradle :buildPlugin
|
||||||
|
# - name: Run Idea
|
||||||
|
# run: |
|
||||||
|
# export DISPLAY=:99.0
|
||||||
|
# Xvfb -ac :99 -screen 0 1920x1080x16 &
|
||||||
|
# mkdir -p build/reports
|
||||||
|
# gradle :runIdeForUiTests #> build/reports/idea.log
|
||||||
|
# - name: Wait for Idea started
|
||||||
|
# uses: jtalk/url-health-check-action@1.5
|
||||||
|
# with:
|
||||||
|
# url: http://127.0.0.1:8082
|
||||||
|
# max-attempts: 15
|
||||||
|
# retry-delay: 30s
|
||||||
|
# - name: Tests
|
||||||
|
# run: gradle :testUi
|
||||||
|
# - name: Save fails report
|
||||||
|
# if: ${{ failure() }}
|
||||||
|
# uses: actions/upload-artifact@v2
|
||||||
|
# with:
|
||||||
|
# name: ui-test-fails-report-linux
|
||||||
|
# path: |
|
||||||
|
# ui-test-example/build/reports
|
@ -360,6 +360,10 @@ Contributors:
|
|||||||
[![icon][github]](https://github.com/MichalPlacek)
|
[![icon][github]](https://github.com/MichalPlacek)
|
||||||
|
|
||||||
Michal Placek
|
Michal Placek
|
||||||
|
* [![icon][mail]](mailto:eugene.nizienko@jetbrains.com)
|
||||||
|
[![icon][github]](https://github.com/nizienko)
|
||||||
|
|
||||||
|
eugene nizienko
|
||||||
|
|
||||||
If you are a contributor and your name is not listed here, feel free to
|
If you are a contributor and your name is not listed here, feel free to
|
||||||
contact the maintainers.
|
contact the maintainers.
|
||||||
|
@ -36,6 +36,7 @@ val kotlinVersion: String by project
|
|||||||
val ideaVersion: String by project
|
val ideaVersion: String by project
|
||||||
val downloadIdeaSources: String by project
|
val downloadIdeaSources: String by project
|
||||||
val instrumentPluginCode: String by project
|
val instrumentPluginCode: String by project
|
||||||
|
val remoteRobotVersion: String by project
|
||||||
|
|
||||||
val publishChannels: String by project
|
val publishChannels: String by project
|
||||||
val publishToken: String by project
|
val publishToken: String by project
|
||||||
@ -55,8 +56,8 @@ dependencies {
|
|||||||
testImplementation("com.ensarsarajcic.neovim.java:neovim-api:0.2.3")
|
testImplementation("com.ensarsarajcic.neovim.java:neovim-api:0.2.3")
|
||||||
testImplementation("com.ensarsarajcic.neovim.java:core-rpc:0.2.3")
|
testImplementation("com.ensarsarajcic.neovim.java:core-rpc:0.2.3")
|
||||||
|
|
||||||
testImplementation("com.intellij.remoterobot:remote-robot:0.11.6")
|
testImplementation("com.intellij.remoterobot:remote-robot:$remoteRobotVersion")
|
||||||
testImplementation("com.intellij.remoterobot:remote-fixtures:1.1.18")
|
testImplementation("com.intellij.remoterobot:remote-fixtures:$remoteRobotVersion")
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- Compilation
|
// --- Compilation
|
||||||
@ -116,7 +117,7 @@ intellij {
|
|||||||
|
|
||||||
tasks {
|
tasks {
|
||||||
downloadRobotServerPlugin {
|
downloadRobotServerPlugin {
|
||||||
version.set("0.10.0")
|
version.set(remoteRobotVersion)
|
||||||
}
|
}
|
||||||
|
|
||||||
publishPlugin {
|
publishPlugin {
|
||||||
|
@ -5,6 +5,7 @@ downloadIdeaSources=true
|
|||||||
instrumentPluginCode=true
|
instrumentPluginCode=true
|
||||||
version=SNAPSHOT
|
version=SNAPSHOT
|
||||||
javaVersion=1.8
|
javaVersion=1.8
|
||||||
|
remoteRobotVersion=0.11.6
|
||||||
|
|
||||||
# Please don't forget to update kotlin version in buildscript section
|
# Please don't forget to update kotlin version in buildscript section
|
||||||
kotlinVersion=1.5.0
|
kotlinVersion=1.5.0
|
||||||
|
@ -23,7 +23,6 @@ import com.intellij.remoterobot.fixtures.ContainerFixture
|
|||||||
import com.intellij.remoterobot.search.locators.byXpath
|
import com.intellij.remoterobot.search.locators.byXpath
|
||||||
import com.intellij.remoterobot.stepsProcessing.step
|
import com.intellij.remoterobot.stepsProcessing.step
|
||||||
import com.intellij.remoterobot.utils.keyboard
|
import com.intellij.remoterobot.utils.keyboard
|
||||||
import com.intellij.remoterobot.utils.waitFor
|
|
||||||
import org.assertj.swing.core.MouseButton
|
import org.assertj.swing.core.MouseButton
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import ui.pages.Editor
|
import ui.pages.Editor
|
||||||
@ -53,7 +52,7 @@ class UiTests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun ideaVimTest() = uiTest {
|
fun ideaVimTest() = uiTest("ideaVimTest") {
|
||||||
val sharedSteps = JavaExampleSteps(this)
|
val sharedSteps = JavaExampleSteps(this)
|
||||||
|
|
||||||
welcomeFrame {
|
welcomeFrame {
|
||||||
@ -69,12 +68,14 @@ class UiTests {
|
|||||||
button("Finish").click()
|
button("Finish").click()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
sharedSteps.closeTipOfTheDay()
|
with(sharedSteps) {
|
||||||
|
closeIdeaVimDialog()
|
||||||
|
closeTipOfTheDay()
|
||||||
|
}
|
||||||
idea {
|
idea {
|
||||||
step("Create App file") {
|
step("Create App file") {
|
||||||
with(projectViewTree) {
|
with(projectViewTree) {
|
||||||
findText(projectName).doubleClick()
|
expand(projectName, "src")
|
||||||
waitFor { hasText("src") }
|
|
||||||
findText("src").click(MouseButton.RIGHT_BUTTON)
|
findText("src").click(MouseButton.RIGHT_BUTTON)
|
||||||
}
|
}
|
||||||
actionMenu("New").click()
|
actionMenu("New").click()
|
||||||
|
@ -54,8 +54,19 @@ class Editor(
|
|||||||
val caretOffset: Int
|
val caretOffset: Int
|
||||||
get() = callJs("component.getEditor().getCaretModel().getOffset()", runInEdt = true)
|
get() = callJs("component.getEditor().getCaretModel().getOffset()", runInEdt = true)
|
||||||
|
|
||||||
|
val isBlockCursor: Boolean
|
||||||
|
get() = callJs("component.getEditor().getSettings().isBlockCursor()", true)
|
||||||
|
|
||||||
fun injectText(text: String) {
|
fun injectText(text: String) {
|
||||||
runJs("component.getEditor().getDocument().setText('${text.escape()}')", runInEdt = true)
|
runJs("""
|
||||||
|
const app = com.intellij.openapi.application.ApplicationManager.getApplication()
|
||||||
|
|
||||||
|
app.invokeLaterOnWriteThread(()=>{
|
||||||
|
app['runWriteAction(com.intellij.openapi.util.Computable)'](()=>{
|
||||||
|
component.getEditor().getDocument().setText('${text.escape()}')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
""")
|
||||||
}
|
}
|
||||||
|
|
||||||
@Suppress("unused")
|
@Suppress("unused")
|
||||||
|
@ -24,6 +24,7 @@ import com.intellij.remoterobot.fixtures.CommonContainerFixture
|
|||||||
import com.intellij.remoterobot.fixtures.ContainerFixture
|
import com.intellij.remoterobot.fixtures.ContainerFixture
|
||||||
import com.intellij.remoterobot.fixtures.DefaultXpath
|
import com.intellij.remoterobot.fixtures.DefaultXpath
|
||||||
import com.intellij.remoterobot.fixtures.FixtureName
|
import com.intellij.remoterobot.fixtures.FixtureName
|
||||||
|
import com.intellij.remoterobot.fixtures.JTreeFixture
|
||||||
import com.intellij.remoterobot.search.locators.byXpath
|
import com.intellij.remoterobot.search.locators.byXpath
|
||||||
import com.intellij.remoterobot.stepsProcessing.step
|
import com.intellij.remoterobot.stepsProcessing.step
|
||||||
import com.intellij.remoterobot.utils.waitFor
|
import com.intellij.remoterobot.utils.waitFor
|
||||||
@ -41,7 +42,7 @@ class IdeaFrame(
|
|||||||
) : CommonContainerFixture(remoteRobot, remoteComponent) {
|
) : CommonContainerFixture(remoteRobot, remoteComponent) {
|
||||||
|
|
||||||
val projectViewTree
|
val projectViewTree
|
||||||
get() = find<ContainerFixture>(byXpath("ProjectViewTree", "//div[@class='ProjectViewTree']"))
|
get() = find<JTreeFixture>(byXpath("ProjectViewTree", "//div[@class='ProjectViewTree']"), Duration.ofSeconds(10))
|
||||||
|
|
||||||
val projectName
|
val projectName
|
||||||
get() = step("Get project name") { return@step callJs<String>("component.getProject().getName()") }
|
get() = step("Get project name") { return@step callJs<String>("component.getProject().getName()") }
|
||||||
|
@ -18,28 +18,48 @@
|
|||||||
package ui.utils
|
package ui.utils
|
||||||
|
|
||||||
import com.intellij.remoterobot.RemoteRobot
|
import com.intellij.remoterobot.RemoteRobot
|
||||||
|
import com.intellij.remoterobot.fixtures.JButtonFixture
|
||||||
|
import com.intellij.remoterobot.search.locators.byXpath
|
||||||
import com.intellij.remoterobot.stepsProcessing.step
|
import com.intellij.remoterobot.stepsProcessing.step
|
||||||
import com.intellij.remoterobot.utils.Keyboard
|
import com.intellij.remoterobot.utils.Keyboard
|
||||||
import ui.pages.DialogFixture
|
import ui.pages.DialogFixture
|
||||||
import ui.pages.DialogFixture.Companion.byTitle
|
import ui.pages.DialogFixture.Companion.byTitle
|
||||||
import ui.pages.IdeaFrame
|
import ui.pages.IdeaFrame
|
||||||
|
import ui.pages.dialog
|
||||||
|
import ui.pages.idea
|
||||||
|
|
||||||
class JavaExampleSteps(private val remoteRobot: RemoteRobot) {
|
class JavaExampleSteps(private val remoteRobot: RemoteRobot) {
|
||||||
@Suppress("unused")
|
@Suppress("unused")
|
||||||
private val keyboard: Keyboard = Keyboard(remoteRobot)
|
private val keyboard: Keyboard = Keyboard(remoteRobot)
|
||||||
|
|
||||||
fun closeTipOfTheDay() {
|
fun closeIdeaVimDialog() = optionalStep("Close Idea Vim dialog if it appears") {
|
||||||
step(
|
remoteRobot.idea {
|
||||||
"Close Tip of the Day if it appears",
|
dialog("IdeaVim") { button("Yes").click() }
|
||||||
Runnable {
|
}
|
||||||
val idea: IdeaFrame = remoteRobot.find(IdeaFrame::class.java)
|
}
|
||||||
idea.dumbAware {
|
|
||||||
try {
|
|
||||||
idea.find(DialogFixture::class.java, byTitle("Tip of the Day")).button("Close").click()
|
fun closeTipOfTheDay() = optionalStep("Close Tip of the Day if it appears") {
|
||||||
} catch (ignore: Throwable) {
|
val idea: IdeaFrame = remoteRobot.find(IdeaFrame::class.java)
|
||||||
}
|
idea.dumbAware {
|
||||||
}
|
idea.find(DialogFixture::class.java, byTitle("Tip of the Day")).button("Close").click()
|
||||||
}
|
}
|
||||||
)
|
closeAllGotIt()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fun closeAllGotIt() = step("Close Got It") {
|
||||||
|
remoteRobot.findAll<JButtonFixture>(byXpath("//div[@accessiblename='Got It']")).forEach {
|
||||||
|
it.click()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private fun optionalStep(stepName: String, code: () -> Unit) = step(stepName) {
|
||||||
|
try {
|
||||||
|
code()
|
||||||
|
} catch (ignore: Throwable) {
|
||||||
|
println("$stepName ignored")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -19,7 +19,54 @@
|
|||||||
package ui.utils
|
package ui.utils
|
||||||
|
|
||||||
import com.intellij.remoterobot.RemoteRobot
|
import com.intellij.remoterobot.RemoteRobot
|
||||||
|
import okhttp3.OkHttpClient
|
||||||
|
import okhttp3.Request
|
||||||
|
import java.awt.image.BufferedImage
|
||||||
|
import java.io.ByteArrayOutputStream
|
||||||
|
import java.io.File
|
||||||
|
import javax.imageio.ImageIO
|
||||||
|
|
||||||
fun uiTest(url: String = "http://127.0.0.1:8082", test: RemoteRobot.() -> Unit) {
|
fun uiTest(testName: String = "test_${System.currentTimeMillis()}", url: String = "http://127.0.0.1:8082", test: RemoteRobot.() -> Unit) {
|
||||||
RemoteRobot(url).apply(test)
|
val remoteRobot = RemoteRobot(url)
|
||||||
|
try {
|
||||||
|
remoteRobot.test()
|
||||||
|
} catch (e: Throwable) {
|
||||||
|
saveScreenshot(testName, remoteRobot)
|
||||||
|
saveHierarchy(testName, url)
|
||||||
|
throw e
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
private val client by lazy { OkHttpClient() }
|
||||||
|
private fun BufferedImage.save(name: String) {
|
||||||
|
val bytes = ByteArrayOutputStream().use { b ->
|
||||||
|
ImageIO.write(this, "png", b)
|
||||||
|
b.toByteArray()
|
||||||
|
}
|
||||||
|
File("build/reports").apply { mkdirs() }.resolve("$name.png").writeBytes(bytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun saveScreenshot(testName: String, remoteRobot: RemoteRobot) {
|
||||||
|
fetchScreenShot(remoteRobot).save(testName)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun fetchScreenShot(remoteRobot: RemoteRobot): BufferedImage {
|
||||||
|
return remoteRobot.getScreenshot()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun saveHierarchy(testName: String, url: String) {
|
||||||
|
val hierarchySnapshot =
|
||||||
|
saveFile(url, "build/reports", "hierarchy-$testName.html")
|
||||||
|
if (File("build/reports/styles.css").exists().not()) {
|
||||||
|
saveFile("$url/styles.css", "build/reports", "styles.css")
|
||||||
|
}
|
||||||
|
println("Hierarchy snapshot: ${hierarchySnapshot.absolutePath}")
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun saveFile(url: String, folder: String, name: String): File {
|
||||||
|
val response = client.newCall(Request.Builder().url(url).build()).execute()
|
||||||
|
return File(folder).apply {
|
||||||
|
mkdirs()
|
||||||
|
}.resolve(name).apply {
|
||||||
|
writeText(response.body?.string() ?: "")
|
||||||
|
}
|
||||||
|
}
|
@ -22,6 +22,7 @@ import com.intellij.remoterobot.fixtures.Fixture
|
|||||||
import com.intellij.remoterobot.fixtures.dataExtractor.RemoteText
|
import com.intellij.remoterobot.fixtures.dataExtractor.RemoteText
|
||||||
import com.intellij.remoterobot.utils.waitFor
|
import com.intellij.remoterobot.utils.waitFor
|
||||||
import org.assertj.swing.core.MouseButton
|
import org.assertj.swing.core.MouseButton
|
||||||
|
import ui.pages.Editor
|
||||||
import java.awt.Point
|
import java.awt.Point
|
||||||
|
|
||||||
fun RemoteText.doubleClickOnRight(shiftX: Int, fixture: Fixture, button: MouseButton = MouseButton.LEFT_BUTTON) {
|
fun RemoteText.doubleClickOnRight(shiftX: Int, fixture: Fixture, button: MouseButton = MouseButton.LEFT_BUTTON) {
|
||||||
@ -38,22 +39,13 @@ fun RemoteText.tripleClickOnRight(shiftX: Int, fixture: Fixture, button: MouseBu
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun RemoteText.moveMouseTo(goal: RemoteText, fixture: Fixture): Boolean {
|
fun RemoteText.moveMouseTo(goal: RemoteText, editor: Editor): Boolean {
|
||||||
this.moveMouse()
|
this.moveMouse()
|
||||||
val goalPoint = goal.point
|
editor.runJs("robot.pressMouse(MouseButton.LEFT_BUTTON)")
|
||||||
|
goal.moveMouse()
|
||||||
val caretDuringDragging = fixture.callJs<Boolean>(
|
val caretDuringDragging = editor.isBlockCursor
|
||||||
"""
|
editor.runJs("robot.releaseMouse(MouseButton.LEFT_BUTTON)")
|
||||||
const point = new java.awt.Point(${goalPoint.x}, ${goalPoint.y});
|
waitFor { editor.isBlockCursor }
|
||||||
let isBlock = true;
|
|
||||||
robot.pressMouseWhileRunning(MouseButton.LEFT_BUTTON, () => {
|
|
||||||
robot.moveMouse(component, point)
|
|
||||||
isBlock = component.getEditor().getSettings().isBlockCursor();
|
|
||||||
})
|
|
||||||
isBlock
|
|
||||||
"""
|
|
||||||
)
|
|
||||||
waitFor { fixture.callJs("component.getEditor().getSettings().isBlockCursor()") }
|
|
||||||
return caretDuringDragging
|
return caretDuringDragging
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -71,22 +63,22 @@ fun RemoteText.moveMouseInGutterTo(goal: RemoteText, fixture: Fixture) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun RemoteText.moveMouseForthAndBack(middle: RemoteText, fixture: Fixture) {
|
fun RemoteText.moveMouseForthAndBack(middle: RemoteText, editor: Editor) {
|
||||||
this.moveMouse()
|
this.moveMouse()
|
||||||
val initialPoint = this.point
|
val initialPoint = this.point
|
||||||
val middlePoint = middle.point
|
val middlePoint = middle.point
|
||||||
|
|
||||||
fixture.runJs(
|
editor.runJs(
|
||||||
"""
|
"""
|
||||||
const initialPoint = new java.awt.Point(${initialPoint.x}, ${initialPoint.y});
|
const initialPoint = new Point(${initialPoint.x}, ${initialPoint.y});
|
||||||
const point = new java.awt.Point(${middlePoint.x}, ${middlePoint.y});
|
const point = new Point(${middlePoint.x}, ${middlePoint.y});
|
||||||
robot.pressMouseWhileRunning(MouseButton.LEFT_BUTTON, () => {
|
robot.pressMouseWhileRunning(MouseButton.LEFT_BUTTON, () => {
|
||||||
robot.moveMouse(component, point)
|
robot.moveMouse(component, point)
|
||||||
robot.moveMouse(component, initialPoint)
|
robot.moveMouse(component, initialPoint)
|
||||||
})
|
})
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
waitFor { fixture.callJs("component.getEditor().getSettings().isBlockCursor()") }
|
waitFor { editor.isBlockCursor }
|
||||||
}
|
}
|
||||||
|
|
||||||
fun String.escape(): String = this.replace("\n", "\\n")
|
fun String.escape(): String = this.replace("\n", "\\n")
|
||||||
|
Loading…
Reference in New Issue
Block a user