@@ -3,7 +3,6 @@ import kotlinx.coroutines.flow.*
3
3
import kotlinx.serialization.*
4
4
import kotlinx.serialization.json.*
5
5
import org.hildan.chrome.devtools.*
6
- import org.hildan.chrome.devtools.domains.accessibility.*
7
6
import org.hildan.chrome.devtools.domains.backgroundservice.*
8
7
import org.hildan.chrome.devtools.domains.dom.*
9
8
import org.hildan.chrome.devtools.domains.domdebugger.*
@@ -14,7 +13,6 @@ import org.hildan.chrome.devtools.protocol.json.*
14
13
import org.hildan.chrome.devtools.sessions.*
15
14
import org.hildan.chrome.devtools.targets.*
16
15
import kotlin.test.*
17
- import kotlin.time.Duration.Companion.minutes
18
16
import kotlin.time.Duration.Companion.seconds
19
17
20
18
abstract class IntegrationTestBase {
@@ -36,113 +34,115 @@ abstract class IntegrationTestBase {
36
34
37
35
protected fun chromeHttp (): ChromeDPHttpApi = ChromeDP .httpApi(httpUrl)
38
36
39
- protected suspend fun chromeWebSocket (): BrowserSession = ChromeDP .connect(wsConnectUrl)
37
+ protected suspend fun RealTimeTestScope.chromeWebSocket (): BrowserSession = ChromeDP .connect(
38
+ wsOrHttpUrl = wsConnectUrl,
39
+ sessionContext = backgroundScope.coroutineContext,
40
+ )
40
41
41
42
@Test
42
- fun httpMetadataEndpoints () {
43
- runBlockingWithTimeout {
44
- val chrome = chromeHttp()
45
-
46
- val version = chrome.version()
47
- assertTrue(version.browser.contains(" Chrome" ))
48
- assertTrue(version.userAgent.contains(" HeadlessChrome" ))
49
- assertTrue(version.webSocketDebuggerUrl.startsWith(" ws://" ), " the debugger URL should start with ws://, but was: ${version.webSocketDebuggerUrl} " )
50
- println (" Chrome version: $version " )
51
-
52
- val protocolJson = chrome.protocolJson()
53
- assertTrue(protocolJson.isNotEmpty(), " the JSON definition of the protocol should not be empty" )
54
- val descriptor = Json .decodeFromString<ChromeProtocolDescriptor >(protocolJson)
55
- println (" Chrome protocol JSON version: ${descriptor.version} " )
56
- }
43
+ fun httpMetadataEndpoints () = runTestWithRealTime {
44
+ val chrome = chromeHttp()
45
+
46
+ val version = chrome.version()
47
+ assertTrue(version.browser.contains(" Chrome" ))
48
+ assertTrue(version.userAgent.contains(" HeadlessChrome" ))
49
+ assertTrue(version.webSocketDebuggerUrl.startsWith(" ws://" ), " the debugger URL should start with ws://, but was: ${version.webSocketDebuggerUrl} " )
50
+ println (" Chrome version: $version " )
51
+
52
+ val protocolJson = chrome.protocolJson()
53
+ assertTrue(protocolJson.isNotEmpty(), " the JSON definition of the protocol should not be empty" )
54
+ val descriptor = Json .decodeFromString<ChromeProtocolDescriptor >(protocolJson)
55
+ println (" Chrome protocol JSON version: ${descriptor.version} " )
57
56
}
58
57
59
58
@OptIn(ExperimentalChromeApi ::class )
60
59
@Test
61
- fun basicFlow_remote () {
62
- runBlockingWithTimeout {
63
- chromeWebSocket().use { browser ->
64
- val pageSession = browser.newPage()
65
- val targetId = pageSession.metaData.targetId
60
+ fun basicFlow_remote () = runTestWithRealTime {
61
+ chromeWebSocket().use { browser ->
62
+ val pageSession = browser.newPage()
63
+ val targetId = pageSession.metaData.targetId
66
64
67
- pageSession.use { page ->
68
- page.goto(" http ://www.google.com" )
65
+ pageSession.use { page ->
66
+ page.goto(" https ://www.google.com" )
69
67
70
- assertEquals(" Google" , page.target.getTargetInfo().targetInfo.title)
68
+ assertEquals(" Google" , page.target.getTargetInfo().targetInfo.title)
71
69
72
- assertTrue(browser.target.getTargets().targetInfos.any { it.targetId == targetId }, " the new target should be listed" )
73
-
74
- val nodeId = withTimeoutOrNull(5 .seconds) {
75
- page.dom.awaitNodeBySelector(" form[action='/search']" )
76
- }
77
- assertNotNull(nodeId, " timed out while waiting for DOM node with attribute: form[action='/search']" )
70
+ assertTrue(browser.hasTarget(targetId), " the new target should be listed" )
78
71
79
- val getOuterHTMLResponse = page.dom.getOuterHTML( GetOuterHTMLRequest ( nodeId = nodeId))
80
- assertTrue(getOuterHTMLResponse.outerHTML.contains( " <input name= \" source \" " ) )
72
+ val nodeId = withTimeoutOrNull( 5 .seconds) {
73
+ page.dom.awaitNodeBySelector( " form[action='/search'] " )
81
74
}
82
- assertTrue(browser.target.getTargets().targetInfos.none { it.targetId == targetId }, " the new target should be closed (not listed)" )
75
+ assertNotNull(nodeId, " timed out while waiting for DOM node with attribute: form[action='/search']" )
76
+
77
+ val getOuterHTMLResponse = page.dom.getOuterHTML(GetOuterHTMLRequest (nodeId = nodeId))
78
+ assertTrue(getOuterHTMLResponse.outerHTML.contains(" <input name=\" source\" " ))
83
79
}
80
+ assertFalse(browser.hasTarget(targetId), " the new target should be closed (not listed)" )
84
81
}
85
82
}
86
83
84
+ protected suspend fun BrowserSession.hasTarget (targetId : String ) =
85
+ target.getTargets().targetInfos.any { it.targetId == targetId }
86
+
87
87
@OptIn(ExperimentalChromeApi ::class )
88
88
@Test
89
- fun supportedDomains_all () {
90
- runBlockingWithTimeout {
91
- val client = chromeHttp( )
92
- val descriptor = Json .decodeFromString< ChromeProtocolDescriptor >(client.protocolJson())
93
-
94
- val actualSupportedDomains = descriptor.domains
95
- .filterNot { it.domain in knownUnsupportedDomains }
96
- .map { it.domain }
97
- .toSet()
98
- val domainsDiff = actualSupportedDomains - knownUnsupportedDomains - AllDomainsTarget .supportedDomains
99
- if (domainsDiff.isNotEmpty()) {
100
- fail( " The library should support all domains that the server actually exposes (apart from " +
101
- " $knownUnsupportedDomains ), but it's missing: ${domainsDiff.sorted()} " )
102
- }
89
+ fun supportedDomains_all () = runTestWithRealTime {
90
+ val client = chromeHttp()
91
+ val descriptor = Json .decodeFromString< ChromeProtocolDescriptor >(client.protocolJson() )
92
+
93
+ val actualSupportedDomains = descriptor.domains
94
+ .filterNot { it.domain in knownUnsupportedDomains }
95
+ .map { it.domain }
96
+ .toSet()
97
+ val domainsDiff = actualSupportedDomains - knownUnsupportedDomains - AllDomainsTarget .supportedDomains
98
+ if ( domainsDiff.isNotEmpty()) {
99
+ fail(
100
+ " The library should support all domains that the server actually exposes (apart from " +
101
+ " $knownUnsupportedDomains ), but it's missing: ${domainsDiff.sorted()} "
102
+ )
103
103
}
104
104
}
105
105
106
106
@OptIn(ExperimentalChromeApi ::class )
107
107
@Test
108
- fun supportedDomains_pageTarget () {
109
- runBlockingWithTimeout {
110
- chromeWebSocket().use { browser ->
111
- browser.newPage().use { page ->
112
- page.accessibility.enable()
113
- page.animation.enable()
114
- page.backgroundService.clearEvents(ServiceName .backgroundFetch)
115
- page.browser.getVersion()
116
- // Commenting this one out until the issue is better understood
117
- // https://github.com/joffrey-bion/chrome-devtools-kotlin/issues/233
118
- // page.cacheStorage.requestCacheNames(RequestCacheNamesRequest("google.com"))
119
- page.css.getMediaQueries()
120
- page.debugger.disable()
121
- page.deviceOrientation.clearDeviceOrientationOverride()
122
- page.domDebugger.setDOMBreakpoint(
123
- nodeId = page.dom.getDocumentRootNodeId(),
124
- type = DOMBreakpointType .attributeModified,
108
+ fun supportedDomains_pageTarget () = runTestWithRealTime {
109
+ chromeWebSocket().use { browser ->
110
+ browser.newPage().use { page ->
111
+ page.accessibility.enable()
112
+ page.animation.enable()
113
+ page.backgroundService.clearEvents(ServiceName .backgroundFetch)
114
+ page.browser.getVersion()
115
+ // Commenting this one out until the issue is better understood
116
+ // https://github.com/joffrey-bion/chrome-devtools-kotlin/issues/233
117
+ // page.cacheStorage.requestCacheNames(RequestCacheNamesRequest("google.com"))
118
+ page.css.getMediaQueries()
119
+ page.debugger.disable()
120
+ page.deviceOrientation.clearDeviceOrientationOverride()
121
+ page.domDebugger.setDOMBreakpoint(
122
+ nodeId = page.dom.getDocumentRootNodeId(),
123
+ type = DOMBreakpointType .attributeModified,
124
+ )
125
+ page.domSnapshot.enable()
126
+ page.domStorage.enable()
127
+ page.fetch.disable()
128
+ page.heapProfiler.enable()
129
+ page.indexedDB.enable()
130
+ page.layerTree.enable()
131
+ page.performance.disable()
132
+ page.profiler.disable()
133
+ page.runtime.enable()
134
+
135
+ // We cannot replace this schema Domain call by an HTTP call to /json/protocol, because
136
+ // the protocol JSON contains the list of all domains, not just for the page target type.
137
+ @Suppress(" DEPRECATION" )
138
+ val actualPageDomains = page.schema.getDomains().domains.map { it.name }.toSet()
139
+
140
+ val pageDomainsDiff = actualPageDomains - knownUnsupportedDomains - PageTarget .supportedDomains
141
+ if (pageDomainsDiff.isNotEmpty()) {
142
+ fail(
143
+ " PageSession should support all domains that the server actually exposes (apart from " +
144
+ " $knownUnsupportedDomains ), but it's missing: ${pageDomainsDiff.sorted()} "
125
145
)
126
- page.domSnapshot.enable()
127
- page.domStorage.enable()
128
- page.fetch.disable()
129
- page.heapProfiler.enable()
130
- page.indexedDB.enable()
131
- page.layerTree.enable()
132
- page.performance.disable()
133
- page.profiler.disable()
134
- page.runtime.enable()
135
-
136
- // We cannot replace this schema Domain call by an HTTP call to /json/protocol, because
137
- // the protocol JSON contains the list of all domains, not just for the page target type.
138
- @Suppress(" DEPRECATION" )
139
- val actualPageDomains = page.schema.getDomains().domains.map { it.name }.toSet()
140
-
141
- val pageDomainsDiff = actualPageDomains - knownUnsupportedDomains - PageTarget .supportedDomains
142
- if (pageDomainsDiff.isNotEmpty()) {
143
- fail(" PageSession should support all domains that the server actually exposes (apart from " +
144
- " $knownUnsupportedDomains ), but it's missing: ${pageDomainsDiff.sorted()} " )
145
- }
146
146
}
147
147
}
148
148
}
@@ -152,46 +152,38 @@ abstract class IntegrationTestBase {
152
152
data class Person (val firstName : String , val lastName : String )
153
153
154
154
@Test
155
- fun runtime_evaluateJs () {
156
- runBlockingWithTimeout {
157
- chromeWebSocket().use { browser ->
158
- browser.newPage().use { page ->
159
- assertEquals(42 , page.runtime.evaluateJs<Int >(" 42" ))
160
- assertEquals(
161
- 42 to " test" ,
162
- page.runtime.evaluateJs<Pair <Int , String >>(""" eval({first: 42, second: "test"})""" )
163
- )
164
- assertEquals(
165
- Person (" Bob" , " Lee Swagger" ),
166
- page.runtime.evaluateJs<Person >(""" eval({firstName: "Bob", lastName: "Lee Swagger"})""" )
167
- )
168
- }
155
+ fun runtime_evaluateJs () = runTestWithRealTime {
156
+ chromeWebSocket().use { browser ->
157
+ browser.newPage().use { page ->
158
+ assertEquals(42 , page.runtime.evaluateJs<Int >(" 42" ))
159
+ assertEquals(
160
+ 42 to " test" ,
161
+ page.runtime.evaluateJs<Pair <Int , String >>(""" eval({first: 42, second: "test"})""" )
162
+ )
163
+ assertEquals(
164
+ Person (" Bob" , " Lee Swagger" ),
165
+ page.runtime.evaluateJs<Person >(""" eval({firstName: "Bob", lastName: "Lee Swagger"})""" )
166
+ )
169
167
}
170
168
}
171
169
}
172
170
173
171
@OptIn(ExperimentalChromeApi ::class )
174
172
@Test
175
- open fun missingExpiresInCookie () {
176
- runBlockingWithTimeout {
177
- chromeWebSocket().use { browser ->
178
- browser.newPage().use { page ->
179
- page.goto(" https://x.com" )
180
- page.network.enable()
181
- coroutineScope {
182
- launch {
183
- // ensures we don't crash on deserialization
184
- page.network.responseReceivedExtraInfoEvents().first()
185
- }
186
- page.dom.awaitNodeBySelector(" a[href=\" /login\" ]" )
187
- page.clickOnElement(" a[href=\" /login\" ]" )
173
+ open fun missingExpiresInCookie () = runTestWithRealTime {
174
+ chromeWebSocket().use { browser ->
175
+ browser.newPage().use { page ->
176
+ page.goto(" https://x.com" )
177
+ page.network.enable()
178
+ coroutineScope {
179
+ launch {
180
+ // ensures we don't crash on deserialization
181
+ page.network.responseReceivedExtraInfoEvents().first()
188
182
}
183
+ page.dom.awaitNodeBySelector(" a[href=\" /login\" ]" )
184
+ page.clickOnElement(" a[href=\" /login\" ]" )
189
185
}
190
186
}
191
187
}
192
188
}
193
-
194
- protected fun runBlockingWithTimeout (block : suspend CoroutineScope .() -> Unit ) = runBlocking {
195
- withTimeout(1 .minutes, block)
196
- }
197
189
}
0 commit comments