Skip to content

Commit

Permalink
Add new webview classes
Browse files Browse the repository at this point in the history
  • Loading branch information
mscwilson committed Nov 25, 2024
1 parent 05fdaa6 commit 6277555
Show file tree
Hide file tree
Showing 4 changed files with 419 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
/*
* Copyright (c) 2015-present Snowplow Analytics Ltd. All rights reserved.
*
* This program is licensed to you under the Apache License Version 2.0,
* and you may not use this file except in compliance with the Apache License Version 2.0.
* You may obtain a copy of the Apache License Version 2.0 at http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the Apache License Version 2.0 is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the Apache License Version 2.0 for the specific language governing permissions and limitations there under.
*/

package com.snowplowanalytics.snowplow.tracker

import android.content.Context
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.platform.app.InstrumentationRegistry
import com.snowplowanalytics.core.constants.Parameters
import com.snowplowanalytics.core.emitter.Executor
import com.snowplowanalytics.core.media.MediaSchemata.eventSchema
import com.snowplowanalytics.core.media.MediaSchemata.playerSchema
import com.snowplowanalytics.core.media.MediaSchemata.sessionSchema
import com.snowplowanalytics.core.media.controller.MediaPingInterval
import com.snowplowanalytics.core.media.controller.MediaSessionTracking
import com.snowplowanalytics.core.media.controller.MediaTrackingImpl
import com.snowplowanalytics.core.media.controller.TimerInterface
import com.snowplowanalytics.core.tracker.TrackerWebViewInterface
import com.snowplowanalytics.core.tracker.TrackerWebViewInterfaceV2
import com.snowplowanalytics.core.utils.Util.objectMapToString
import com.snowplowanalytics.snowplow.Snowplow.createTracker
import com.snowplowanalytics.snowplow.Snowplow.removeAllTrackers
import com.snowplowanalytics.snowplow.configuration.NetworkConfiguration
import com.snowplowanalytics.snowplow.configuration.PluginConfiguration
import com.snowplowanalytics.snowplow.configuration.TrackerConfiguration
import com.snowplowanalytics.snowplow.controller.TrackerController
import com.snowplowanalytics.snowplow.event.SelfDescribing
import com.snowplowanalytics.snowplow.media.configuration.MediaTrackingConfiguration
import com.snowplowanalytics.snowplow.media.entity.MediaPlayerEntity
import com.snowplowanalytics.snowplow.media.event.*
import com.snowplowanalytics.snowplow.network.HttpMethod
import com.snowplowanalytics.snowplow.payload.SelfDescribingJson
import com.snowplowanalytics.snowplow.tracker.InspectableEvent
import com.snowplowanalytics.snowplow.tracker.MockNetworkConnection
import com.snowplowanalytics.snowplow.util.TimeTraveler
import junit.framework.TestCase
import org.json.JSONException
import org.junit.After
import org.junit.Assert.*
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import java.util.*
import kotlin.time.DurationUnit
import kotlin.time.toDuration

@RunWith(AndroidJUnit4::class)
class TrackerWebViewInterfaceV2Test {
private var webInterface: TrackerWebViewInterfaceV2? = null
private val trackedEvents: MutableList<InspectableEvent> = mutableListOf()
private val firstEvent: InspectableEvent
get() = trackedEvents.first()
private var tracker: TrackerController? = null

@Before
fun setUp() {
webInterface = TrackerWebViewInterfaceV2()
tracker = createTracker()
}

@After
fun tearDown() {
tracker?.pause()
tracker = null
removeAllTrackers()
trackedEvents.clear()
Executor.shutdown()
}

@Test
@Throws(JSONException::class, InterruptedException::class)
fun tracksPagePingEvent() {
webInterface!!.trackWebViewEvent(
eventName = "pp",
trackerVersion = "webview",
useragent = "Firefox",
pageUrl = "http://snowplow.com",
pageTitle = "Snowplow",
referrer = "http://google.com",
ping_xoffset_min = 10,
ping_xoffset_max = 20,
ping_yoffset_min = 30,
ping_yoffset_max = 40
)

Thread.sleep(200)

assertEquals(1, trackedEvents.size)
val payload = firstEvent.payload
assertEquals("pp", payload[Parameters.EVENT])
assertEquals("webview", payload[Parameters.TRACKER_VERSION])
assertEquals("Firefox", payload[Parameters.USERAGENT])
assertEquals("http://snowplow.com", payload[Parameters.PAGE_URL])
assertEquals("Snowplow", payload[Parameters.PAGE_TITLE])
assertEquals("http://google.com", payload[Parameters.PAGE_REFR])
assertEquals(10, payload[Parameters.PING_XOFFSET_MIN])
assertEquals(20, payload[Parameters.PING_XOFFSET_MAX])
assertEquals(30, payload[Parameters.PING_YOFFSET_MIN])
assertEquals(40, payload[Parameters.PING_YOFFSET_MAX])
}

@Test
@Throws(JSONException::class, InterruptedException::class)
fun tracksStructuredEvent() {
webInterface!!.trackWebViewEvent(
eventName = "se",
trackerVersion = "webview2",
useragent = "Firefox",
category = "cat",
action = "act",
property = "prop",
label = "lbl",
value = 10.0
)

Thread.sleep(200)

assertEquals(1, trackedEvents.size)
val payload = firstEvent.payload
assertEquals("se", payload[Parameters.EVENT])
assertEquals("webview2", payload[Parameters.TRACKER_VERSION])
assertEquals("Firefox", payload[Parameters.USERAGENT])
assertEquals("cat", payload[Parameters.SE_CATEGORY])
assertEquals("act", payload[Parameters.SE_ACTION])
assertEquals("prop", payload[Parameters.SE_PROPERTY])
assertEquals("lbl", payload[Parameters.SE_LABEL])
assertEquals(10.0, payload[Parameters.SE_VALUE])
}


// --- PRIVATE
private val context: Context
get() = InstrumentationRegistry.getInstrumentation().targetContext

private fun createTracker(): TrackerController {
val namespace = "ns" + Math.random().toString()
val networkConfig = NetworkConfiguration(MockNetworkConnection(HttpMethod.POST, 200))
val trackerConfig = TrackerConfiguration("appId")
.installAutotracking(false)
.lifecycleAutotracking(false)

val plugin = PluginConfiguration("plugin")
plugin.afterTrack {
if (namespace == this.tracker?.namespace) {
trackedEvents.add(it)
}
}

return createTracker(
context,
namespace = namespace,
network = networkConfig,
trackerConfig,
plugin
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -259,4 +259,10 @@ object Parameters {
const val DIAGNOSTIC_ERROR_STACK = "stackTrace"
const val DIAGNOSTIC_ERROR_CLASS_NAME = "className"
const val DIAGNOSTIC_ERROR_EXCEPTION_NAME = "exceptionName"

// Page Pings (for WebView tracking)
const val PING_XOFFSET_MIN = "pp_mix"
const val PING_XOFFSET_MAX = "pp_max"
const val PING_YOFFSET_MIN = "pp_miy"
const val PING_YOFFSET_MAX = "pp_may"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
/*
* Copyright (c) 2015-present Snowplow Analytics Ltd. All rights reserved.
*
* This program is licensed to you under the Apache License Version 2.0,
* and you may not use this file except in compliance with the Apache License Version 2.0.
* You may obtain a copy of the Apache License Version 2.0 at http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the Apache License Version 2.0 is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the Apache License Version 2.0 for the specific language governing permissions and limitations there under.
*/
package com.snowplowanalytics.core.event

import com.snowplowanalytics.core.constants.Parameters
import com.snowplowanalytics.core.constants.TrackerConstants
import com.snowplowanalytics.snowplow.event.AbstractPrimitive

/**
* A PageView event. This event has been designed for web trackers, and is not suitable for mobile apps.
* @param pageUrl The page URL.
*/
class WebViewReader(
val eventName: String,
val trackerVersion: String,
val useragent: String,
val pageUrl: String? = null,
val pageTitle: String? = null,
val referrer: String? = null,
val category: String? = null,
val action: String? = null,
val label: String? = null,
val property: String? = null,
val value: Double? = null,
val ping_xoffset_min: Int? = null,
val ping_xoffset_max: Int? = null,
val ping_yoffset_min: Int? = null,
val ping_yoffset_max: Int? = null
) : AbstractPrimitive() {


// Public methods
override val dataPayload: Map<String, Any?>
get() {
val payload = HashMap<String, Any?>()
payload[Parameters.EVENT] = eventName
payload[Parameters.TRACKER_VERSION] = trackerVersion
payload[Parameters.USERAGENT] = useragent
if (pageUrl != null) payload[Parameters.PAGE_URL] = pageUrl
if (pageTitle != null) payload[Parameters.PAGE_TITLE] = pageTitle
if (referrer != null) payload[Parameters.PAGE_REFR] = referrer
if (category != null) payload[Parameters.SE_CATEGORY] = category
if (action != null) payload[Parameters.SE_ACTION] = action
if (label != null) payload[Parameters.SE_LABEL] = label
if (property != null) payload[Parameters.SE_PROPERTY] = property
if (value != null) payload[Parameters.SE_VALUE] = value
if (ping_xoffset_min != null) payload[Parameters.PING_XOFFSET_MIN] = ping_xoffset_min
if (ping_xoffset_max != null) payload[Parameters.PING_XOFFSET_MAX] = ping_xoffset_max
if (ping_yoffset_min != null) payload[Parameters.PING_YOFFSET_MIN] = ping_yoffset_min
if (ping_yoffset_max != null) payload[Parameters.PING_YOFFSET_MAX] = ping_yoffset_max
return payload
}

override val name: String
get() = "internal_use_only"
}
Loading

0 comments on commit 6277555

Please sign in to comment.