Skip to content

Commit

Permalink
fixed Ide Fatal Errors
Browse files Browse the repository at this point in the history
added UI tests reporting
  • Loading branch information
nizienko authored and AlexPl292 committed Jul 23, 2021
1 parent 4158bf1 commit a58ca80
Show file tree
Hide file tree
Showing 10 changed files with 189 additions and 45 deletions.
66 changes: 66 additions & 0 deletions .github/workflows/runUiTests.yml
Original file line number Diff line number Diff line change
@@ -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
4 changes: 4 additions & 0 deletions AUTHORS.md
Original file line number Diff line number Diff line change
Expand Up @@ -360,6 +360,10 @@ Contributors:
[![icon][github]](https://github.com/MichalPlacek)
 
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
contact the maintainers.
Expand Down
7 changes: 4 additions & 3 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ val kotlinVersion: String by project
val ideaVersion: String by project
val downloadIdeaSources: String by project
val instrumentPluginCode: String by project
val remoteRobotVersion: String by project

val publishChannels: String by project
val publishToken: String by project
Expand All @@ -55,8 +56,8 @@ dependencies {
testImplementation("com.ensarsarajcic.neovim.java:neovim-api: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-fixtures:1.1.18")
testImplementation("com.intellij.remoterobot:remote-robot:$remoteRobotVersion")
testImplementation("com.intellij.remoterobot:remote-fixtures:$remoteRobotVersion")
}

// --- Compilation
Expand Down Expand Up @@ -116,7 +117,7 @@ intellij {

tasks {
downloadRobotServerPlugin {
version.set("0.10.0")
version.set(remoteRobotVersion)
}

publishPlugin {
Expand Down
1 change: 1 addition & 0 deletions gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ downloadIdeaSources=true
instrumentPluginCode=true
version=SNAPSHOT
javaVersion=1.8
remoteRobotVersion=0.11.6

# Please don't forget to update kotlin version in buildscript section
kotlinVersion=1.5.0
Expand Down
11 changes: 6 additions & 5 deletions test/ui/UiTests.kt
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ import com.intellij.remoterobot.fixtures.ContainerFixture
import com.intellij.remoterobot.search.locators.byXpath
import com.intellij.remoterobot.stepsProcessing.step
import com.intellij.remoterobot.utils.keyboard
import com.intellij.remoterobot.utils.waitFor
import org.assertj.swing.core.MouseButton
import org.junit.Test
import ui.pages.Editor
Expand Down Expand Up @@ -53,7 +52,7 @@ class UiTests {
}

@Test
fun ideaVimTest() = uiTest {
fun ideaVimTest() = uiTest("ideaVimTest") {
val sharedSteps = JavaExampleSteps(this)

welcomeFrame {
Expand All @@ -69,12 +68,14 @@ class UiTests {
button("Finish").click()
}
}
sharedSteps.closeTipOfTheDay()
with(sharedSteps) {
closeIdeaVimDialog()
closeTipOfTheDay()
}
idea {
step("Create App file") {
with(projectViewTree) {
findText(projectName).doubleClick()
waitFor { hasText("src") }
expand(projectName, "src")
findText("src").click(MouseButton.RIGHT_BUTTON)
}
actionMenu("New").click()
Expand Down
13 changes: 12 additions & 1 deletion test/ui/pages/Editor.kt
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,19 @@ class Editor(
val caretOffset: Int
get() = callJs("component.getEditor().getCaretModel().getOffset()", runInEdt = true)

val isBlockCursor: Boolean
get() = callJs("component.getEditor().getSettings().isBlockCursor()", true)

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")
Expand Down
3 changes: 2 additions & 1 deletion test/ui/pages/IdeaFrame.kt
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import com.intellij.remoterobot.fixtures.CommonContainerFixture
import com.intellij.remoterobot.fixtures.ContainerFixture
import com.intellij.remoterobot.fixtures.DefaultXpath
import com.intellij.remoterobot.fixtures.FixtureName
import com.intellij.remoterobot.fixtures.JTreeFixture
import com.intellij.remoterobot.search.locators.byXpath
import com.intellij.remoterobot.stepsProcessing.step
import com.intellij.remoterobot.utils.waitFor
Expand All @@ -41,7 +42,7 @@ class IdeaFrame(
) : CommonContainerFixture(remoteRobot, remoteComponent) {

val projectViewTree
get() = find<ContainerFixture>(byXpath("ProjectViewTree", "//div[@class='ProjectViewTree']"))
get() = find<JTreeFixture>(byXpath("ProjectViewTree", "//div[@class='ProjectViewTree']"), Duration.ofSeconds(10))

val projectName
get() = step("Get project name") { return@step callJs<String>("component.getProject().getName()") }
Expand Down
46 changes: 33 additions & 13 deletions test/ui/utils/JavaExampleSteps.kt
Original file line number Diff line number Diff line change
Expand Up @@ -18,28 +18,48 @@
package ui.utils

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.utils.Keyboard
import ui.pages.DialogFixture
import ui.pages.DialogFixture.Companion.byTitle
import ui.pages.IdeaFrame
import ui.pages.dialog
import ui.pages.idea

class JavaExampleSteps(private val remoteRobot: RemoteRobot) {
@Suppress("unused")
private val keyboard: Keyboard = Keyboard(remoteRobot)

fun closeTipOfTheDay() {
step(
"Close Tip of the Day if it appears",
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()
} catch (ignore: Throwable) {
}
}
}
)
fun closeIdeaVimDialog() = optionalStep("Close Idea Vim dialog if it appears") {
remoteRobot.idea {
dialog("IdeaVim") { button("Yes").click() }
}
}


fun closeTipOfTheDay() = optionalStep("Close Tip of the Day if it appears") {
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")
}
}
}
51 changes: 49 additions & 2 deletions test/ui/utils/UiTestWrapper.kt
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,54 @@
package ui.utils

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) {
RemoteRobot(url).apply(test)
fun uiTest(testName: String = "test_${System.currentTimeMillis()}", url: String = "http://127.0.0.1:8082", test: RemoteRobot.() -> Unit) {
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() ?: "")
}
}
32 changes: 12 additions & 20 deletions test/ui/utils/Utils.kt
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import com.intellij.remoterobot.fixtures.Fixture
import com.intellij.remoterobot.fixtures.dataExtractor.RemoteText
import com.intellij.remoterobot.utils.waitFor
import org.assertj.swing.core.MouseButton
import ui.pages.Editor
import java.awt.Point

fun RemoteText.doubleClickOnRight(shiftX: Int, fixture: Fixture, button: MouseButton = MouseButton.LEFT_BUTTON) {
Expand All @@ -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()
val goalPoint = goal.point

val caretDuringDragging = fixture.callJs<Boolean>(
"""
const point = new java.awt.Point(${goalPoint.x}, ${goalPoint.y});
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()") }
editor.runJs("robot.pressMouse(MouseButton.LEFT_BUTTON)")
goal.moveMouse()
val caretDuringDragging = editor.isBlockCursor
editor.runJs("robot.releaseMouse(MouseButton.LEFT_BUTTON)")
waitFor { editor.isBlockCursor }
return caretDuringDragging
}

Expand All @@ -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()
val initialPoint = this.point
val middlePoint = middle.point

fixture.runJs(
editor.runJs(
"""
const initialPoint = new java.awt.Point(${initialPoint.x}, ${initialPoint.y});
const point = new java.awt.Point(${middlePoint.x}, ${middlePoint.y});
const initialPoint = new Point(${initialPoint.x}, ${initialPoint.y});
const point = new Point(${middlePoint.x}, ${middlePoint.y});
robot.pressMouseWhileRunning(MouseButton.LEFT_BUTTON, () => {
robot.moveMouse(component, point)
robot.moveMouse(component, initialPoint)
})
"""
)
waitFor { fixture.callJs("component.getEditor().getSettings().isBlockCursor()") }
waitFor { editor.isBlockCursor }
}

fun String.escape(): String = this.replace("\n", "\\n")

0 comments on commit a58ca80

Please sign in to comment.