Skip to content

Commit 73ea3a0

Browse files
committed
Move integration tests to projector-client
1 parent ebb3435 commit 73ea3a0

15 files changed

+885
-0
lines changed

.idea/runConfigurations/Test_integration___integrationTest_.xml

+21
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

gradle.properties

+3
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@
2121
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
2222
# SOFTWARE.
2323
#
24+
coroutinesVersion=1.3.8
2425
kotlinVersion=1.4.0
26+
ktorVersion=1.3.2
27+
selenideVersion=5.13.1
2528
serializationVersion=1.0.0-RC
2629
targetJvm=1.8

projector-server-core/.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
src/intTest/resources/fonts
+110
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
/*
2+
* MIT License
3+
*
4+
* Copyright (c) 2019-2020 JetBrains s.r.o.
5+
*
6+
* Permission is hereby granted, free of charge, to any person obtaining a copy
7+
* of this software and associated documentation files (the "Software"), to deal
8+
* in the Software without restriction, including without limitation the rights
9+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10+
* copies of the Software, and to permit persons to whom the Software is
11+
* furnished to do so, subject to the following conditions:
12+
*
13+
* The above copyright notice and this permission notice shall be included in all
14+
* copies or substantial portions of the Software.
15+
*
16+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22+
* SOFTWARE.
23+
*/
24+
import java.net.URL
25+
import java.util.zip.ZipFile
26+
27+
plugins {
28+
kotlin("jvm")
29+
`maven-publish`
30+
idea
31+
}
32+
33+
val coroutinesVersion: String by project
34+
val kotlinVersion: String by project
35+
val ktorVersion: String by project
36+
val selenideVersion: String by project
37+
38+
val downloadIntTestFont = task("downloadIntTestFont") {
39+
doLast {
40+
val defaultDownloadLink = "https://assets.ubuntu.com/v1/0cef8205-ubuntu-font-family-0.83.zip"
41+
val fontPath = "src/intTest/resources/fonts/intTestFont.ttf"
42+
val destFile = project.file(fontPath)
43+
44+
if (destFile.exists()) {
45+
println("Int test font already exists, skipping.")
46+
}
47+
else {
48+
println("Downloading int test font...")
49+
50+
project.file(fontPath.substringBeforeLast('/')).mkdirs()
51+
52+
val tempFile = File.createTempFile("defaultIntTestFonts", "zip")
53+
URL(defaultDownloadLink).openStream().transferTo(tempFile.outputStream())
54+
55+
ZipFile(tempFile).let {
56+
it.getInputStream(it.getEntry("ubuntu-font-family-0.83/Ubuntu-R.ttf")).transferTo(destFile.outputStream())
57+
}
58+
59+
tempFile.delete()
60+
61+
println("Download complete")
62+
}
63+
}
64+
}
65+
66+
val intTestSourceSetName = "intTest"
67+
68+
sourceSets {
69+
create(intTestSourceSetName) {
70+
compileClasspath += sourceSets.main.get().output
71+
runtimeClasspath += sourceSets.main.get().output
72+
}
73+
}
74+
75+
val intTestImplementation: Configuration by configurations.getting {
76+
extendsFrom(configurations.implementation.get())
77+
}
78+
79+
configurations["intTestRuntimeOnly"].extendsFrom(configurations.runtimeOnly.get())
80+
81+
val integrationTest = task<Test>("integrationTest") {
82+
description = "Runs integration tests."
83+
group = "verification"
84+
85+
testClassesDirs = sourceSets[intTestSourceSetName].output.classesDirs
86+
classpath = sourceSets[intTestSourceSetName].runtimeClasspath
87+
88+
shouldRunAfter("test")
89+
dependsOn(":projector-client-web:browserProductionWebpack", downloadIntTestFont)
90+
}
91+
92+
tasks.check { dependsOn(integrationTest) }
93+
94+
dependencies {
95+
implementation(project(":projector-common"))
96+
97+
// todo: remove these dependencies: they should be exported from projector-common but now it seems not working
98+
testImplementation(kotlin("test", kotlinVersion))
99+
testImplementation(kotlin("test-junit", kotlinVersion))
100+
101+
intTestImplementation("com.codeborne:selenide:$selenideVersion")
102+
intTestImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutinesVersion")
103+
intTestImplementation("io.ktor:ktor-server-core:$ktorVersion")
104+
intTestImplementation("io.ktor:ktor-server-netty:$ktorVersion")
105+
intTestImplementation("io.ktor:ktor-websockets:$ktorVersion")
106+
107+
// todo: remove these dependencies: they should be exported from projector-common but now it seems not working
108+
intTestImplementation("org.jetbrains.kotlin:kotlin-test:$kotlinVersion")
109+
intTestImplementation("org.jetbrains.kotlin:kotlin-test-junit:$kotlinVersion")
110+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
/*
2+
* MIT License
3+
*
4+
* Copyright (c) 2019-2020 JetBrains s.r.o.
5+
*
6+
* Permission is hereby granted, free of charge, to any person obtaining a copy
7+
* of this software and associated documentation files (the "Software"), to deal
8+
* in the Software without restriction, including without limitation the rights
9+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10+
* copies of the Software, and to permit persons to whom the Software is
11+
* furnished to do so, subject to the following conditions:
12+
*
13+
* The above copyright notice and this permission notice shall be included in all
14+
* copies or substantial portions of the Software.
15+
*
16+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22+
* SOFTWARE.
23+
*/
24+
package org.jetbrains.projector.intTest
25+
26+
import com.codeborne.selenide.Condition.appear
27+
import com.codeborne.selenide.Condition.text
28+
import com.codeborne.selenide.Selenide.*
29+
import io.ktor.http.cio.websocket.close
30+
import kotlinx.coroutines.channels.Channel
31+
import kotlinx.coroutines.runBlocking
32+
import org.jetbrains.projector.common.protocol.data.CommonRectangle
33+
import org.jetbrains.projector.common.protocol.toClient.ServerWindowSetChangedEvent
34+
import org.jetbrains.projector.common.protocol.toClient.WindowData
35+
import org.jetbrains.projector.common.protocol.toClient.WindowType
36+
import org.jetbrains.projector.intTest.ConnectionUtil.clientUrl
37+
import org.jetbrains.projector.intTest.ConnectionUtil.startServerAndDoHandshake
38+
import kotlin.test.Test
39+
import kotlin.test.assertFalse
40+
import kotlin.test.assertTrue
41+
42+
class CloseBlockingTest {
43+
44+
private companion object {
45+
46+
private fun openClientAndActivatePage() {
47+
open(clientUrl)
48+
element("body").click(5, 5) // enable onbeforeunload listener, can't click without arguments because of an exception
49+
}
50+
51+
private fun isAlertPresent(): Boolean {
52+
try {
53+
switchTo().alert()
54+
return true
55+
}
56+
catch (e: Throwable) {
57+
if ("NoAlertPresentException" == e::class.java.simpleName) {
58+
return false
59+
}
60+
61+
throw IllegalStateException("Unexpected exception while checking for alert existence", e)
62+
}
63+
}
64+
}
65+
66+
@Test
67+
fun shouldBeAbleToCloseBefore() {
68+
openClientAndActivatePage()
69+
element("body").shouldHave(text("reconnect"))
70+
refresh()
71+
assertFalse(isAlertPresent())
72+
}
73+
74+
@Test
75+
fun shouldBeUnableToCloseWhenConnected() {
76+
val clientLoadNotifier = Channel<Unit>()
77+
78+
val server = startServerAndDoHandshake { (sender, _) ->
79+
val window = WindowData(
80+
id = 1,
81+
isShowing = true,
82+
zOrder = 0,
83+
bounds = CommonRectangle(10.0, 10.0, 100.0, 100.0),
84+
resizable = true,
85+
modal = false,
86+
undecorated = false,
87+
windowType = WindowType.IDEA_WINDOW
88+
)
89+
90+
sender(listOf(ServerWindowSetChangedEvent(listOf(window))))
91+
92+
clientLoadNotifier.send(Unit)
93+
94+
for (frame in incoming) {
95+
// maintaining connection
96+
}
97+
}
98+
server.start()
99+
100+
openClientAndActivatePage()
101+
102+
runBlocking {
103+
clientLoadNotifier.receive()
104+
}
105+
element("canvas.window").should(appear)
106+
107+
refresh()
108+
assertTrue(isAlertPresent())
109+
confirm()
110+
111+
server.stop(500, 1000)
112+
}
113+
114+
@Test
115+
fun shouldBeAbleToCloseAfterConnectionEnds() {
116+
val connectionEndedNotifier = Channel<Unit>()
117+
118+
val server = startServerAndDoHandshake {
119+
close()
120+
connectionEndedNotifier.send(Unit)
121+
}
122+
server.start()
123+
124+
openClientAndActivatePage()
125+
126+
runBlocking {
127+
connectionEndedNotifier.receive()
128+
}
129+
element("body").shouldHave(text("ended"))
130+
131+
refresh()
132+
assertFalse(isAlertPresent())
133+
134+
server.stop(500, 1000)
135+
}
136+
}

0 commit comments

Comments
 (0)