Fix some quoted messages having 'null' message bodies (#7532)
* Fix some quoted messages having 'null' message bodies
This commit is contained in:
parent
0d3c779455
commit
a476544761
1
changelog.d/7530.sdk
Normal file
1
changelog.d/7530.sdk
Normal file
@ -0,0 +1 @@
|
|||||||
|
Fix a bug that caused messages with no formatted text to be quoted as "null".
|
||||||
@ -804,20 +804,12 @@ internal class LocalEchoEventFactory @Inject constructor(
|
|||||||
additionalContent: Content? = null,
|
additionalContent: Content? = null,
|
||||||
): Event {
|
): Event {
|
||||||
val messageContent = quotedEvent.getLastMessageContent()
|
val messageContent = quotedEvent.getLastMessageContent()
|
||||||
val textMsg = if (messageContent is MessageContentWithFormattedBody) {
|
val formattedQuotedText = (messageContent as? MessageContentWithFormattedBody)?.formattedBody
|
||||||
messageContent.formattedBody
|
val textContent = createQuoteTextContent(messageContent?.body, formattedQuotedText, text, formattedText, autoMarkdown)
|
||||||
} else {
|
|
||||||
messageContent?.body
|
|
||||||
}
|
|
||||||
val quoteText = legacyRiotQuoteText(textMsg, text)
|
|
||||||
val quoteFormattedText = "<blockquote>$textMsg</blockquote>$formattedText"
|
|
||||||
|
|
||||||
return if (rootThreadEventId != null) {
|
return if (rootThreadEventId != null) {
|
||||||
createMessageEvent(
|
createMessageEvent(
|
||||||
roomId,
|
roomId,
|
||||||
markdownParser
|
textContent.toThreadTextContent(
|
||||||
.parse(quoteText, force = true, advanced = autoMarkdown).copy(formattedText = quoteFormattedText)
|
|
||||||
.toThreadTextContent(
|
|
||||||
rootThreadEventId = rootThreadEventId,
|
rootThreadEventId = rootThreadEventId,
|
||||||
latestThreadEventId = localEchoRepository.getLatestThreadEvent(rootThreadEventId),
|
latestThreadEventId = localEchoRepository.getLatestThreadEvent(rootThreadEventId),
|
||||||
msgType = MessageType.MSGTYPE_TEXT
|
msgType = MessageType.MSGTYPE_TEXT
|
||||||
@ -827,31 +819,54 @@ internal class LocalEchoEventFactory @Inject constructor(
|
|||||||
} else {
|
} else {
|
||||||
createFormattedTextEvent(
|
createFormattedTextEvent(
|
||||||
roomId,
|
roomId,
|
||||||
markdownParser.parse(quoteText, force = true, advanced = autoMarkdown).copy(formattedText = quoteFormattedText),
|
textContent,
|
||||||
MessageType.MSGTYPE_TEXT,
|
MessageType.MSGTYPE_TEXT,
|
||||||
additionalContent,
|
additionalContent,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun legacyRiotQuoteText(quotedText: String?, myText: String): String {
|
private fun createQuoteTextContent(
|
||||||
val messageParagraphs = quotedText?.split("\n\n".toRegex())?.dropLastWhile { it.isEmpty() }?.toTypedArray()
|
quotedText: String?,
|
||||||
return buildString {
|
formattedQuotedText: String?,
|
||||||
if (messageParagraphs != null) {
|
text: String,
|
||||||
for (i in messageParagraphs.indices) {
|
formattedText: String?,
|
||||||
if (messageParagraphs[i].isNotBlank()) {
|
autoMarkdown: Boolean
|
||||||
append("> ")
|
): TextContent {
|
||||||
append(messageParagraphs[i])
|
val currentFormattedText = formattedText ?: if (autoMarkdown) {
|
||||||
}
|
val parsed = markdownParser.parse(text, force = true, advanced = true)
|
||||||
|
// If formattedText == text, formattedText is returned as null
|
||||||
|
parsed.formattedText ?: parsed.text
|
||||||
|
} else {
|
||||||
|
text
|
||||||
|
}
|
||||||
|
val processedFormattedQuotedText = formattedQuotedText ?: quotedText
|
||||||
|
|
||||||
if (i != messageParagraphs.lastIndex) {
|
val plainTextBody = buildString {
|
||||||
append("\n\n")
|
val plainMessageParagraphs = quotedText?.split("\n\n".toRegex())?.dropLastWhile { it.isEmpty() }?.toTypedArray().orEmpty()
|
||||||
}
|
plainMessageParagraphs.forEachIndexed { index, paragraph ->
|
||||||
|
if (paragraph.isNotBlank()) {
|
||||||
|
append("> ")
|
||||||
|
append(paragraph)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (index != plainMessageParagraphs.lastIndex) {
|
||||||
|
append("\n\n")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
append("\n\n")
|
append("\n\n")
|
||||||
append(myText)
|
append(text)
|
||||||
}
|
}
|
||||||
|
val formattedTextBody = buildString {
|
||||||
|
if (!processedFormattedQuotedText.isNullOrBlank()) {
|
||||||
|
append("<blockquote>")
|
||||||
|
append(processedFormattedQuotedText)
|
||||||
|
append("</blockquote>")
|
||||||
|
}
|
||||||
|
append("<br/>")
|
||||||
|
append(currentFormattedText)
|
||||||
|
}
|
||||||
|
return TextContent(plainTextBody, formattedTextBody)
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|||||||
@ -0,0 +1,241 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2022 The Matrix.org Foundation C.I.C.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.matrix.android.sdk.internal.session.room.send
|
||||||
|
|
||||||
|
import org.amshove.kluent.internal.assertEquals
|
||||||
|
import org.junit.Before
|
||||||
|
import org.junit.Test
|
||||||
|
import org.matrix.android.sdk.api.session.events.model.Event
|
||||||
|
import org.matrix.android.sdk.api.session.events.model.EventType
|
||||||
|
import org.matrix.android.sdk.api.session.events.model.toContent
|
||||||
|
import org.matrix.android.sdk.api.session.events.model.toModel
|
||||||
|
import org.matrix.android.sdk.api.session.room.model.EditAggregatedSummary
|
||||||
|
import org.matrix.android.sdk.api.session.room.model.EventAnnotationsSummary
|
||||||
|
import org.matrix.android.sdk.api.session.room.model.message.MessageContent
|
||||||
|
import org.matrix.android.sdk.api.session.room.model.message.MessageContentWithFormattedBody
|
||||||
|
import org.matrix.android.sdk.api.session.room.sender.SenderInfo
|
||||||
|
import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent
|
||||||
|
import org.matrix.android.sdk.api.util.TextContent
|
||||||
|
import org.matrix.android.sdk.test.fakes.FakeClock
|
||||||
|
import org.matrix.android.sdk.test.fakes.FakeContext
|
||||||
|
import org.matrix.android.sdk.test.fakes.internal.session.content.FakeThumbnailExtractor
|
||||||
|
import org.matrix.android.sdk.test.fakes.internal.session.permalinks.FakePermalinkFactory
|
||||||
|
import org.matrix.android.sdk.test.fakes.internal.session.room.send.FakeLocalEchoRepository
|
||||||
|
import org.matrix.android.sdk.test.fakes.internal.session.room.send.FakeMarkdownParser
|
||||||
|
import org.matrix.android.sdk.test.fakes.internal.session.room.send.FakeWaveFormSanitizer
|
||||||
|
import org.matrix.android.sdk.test.fakes.internal.session.room.send.pills.FakeTextPillsUtils
|
||||||
|
|
||||||
|
@Suppress("MaxLineLength")
|
||||||
|
class LocalEchoEventFactoryTests {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
internal const val A_USER_ID_1 = "@user_1:matrix.org"
|
||||||
|
internal const val A_ROOM_ID = "!sUeOGZKsBValPTUMax:matrix.org"
|
||||||
|
internal const val AN_EVENT_ID = "\$vApgexcL8Vfh-WxYKsFKCDooo67ttbjm3TiVKXaWijU"
|
||||||
|
internal const val AN_EPOCH = 1655210176L
|
||||||
|
|
||||||
|
val A_START_EVENT = Event(
|
||||||
|
type = EventType.STATE_ROOM_CREATE,
|
||||||
|
eventId = AN_EVENT_ID,
|
||||||
|
originServerTs = 1652435922563,
|
||||||
|
senderId = A_USER_ID_1,
|
||||||
|
roomId = A_ROOM_ID
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private val fakeContext = FakeContext()
|
||||||
|
private val fakeMarkdownParser = FakeMarkdownParser()
|
||||||
|
private val fakeTextPillsUtils = FakeTextPillsUtils()
|
||||||
|
private val fakeThumbnailExtractor = FakeThumbnailExtractor()
|
||||||
|
private val fakeWaveFormSanitizer = FakeWaveFormSanitizer()
|
||||||
|
private val fakeLocalEchoRepository = FakeLocalEchoRepository()
|
||||||
|
private val fakePermalinkFactory = FakePermalinkFactory()
|
||||||
|
private val fakeClock = FakeClock()
|
||||||
|
|
||||||
|
private val localEchoEventFactory = LocalEchoEventFactory(
|
||||||
|
context = fakeContext.instance,
|
||||||
|
userId = A_USER_ID_1,
|
||||||
|
markdownParser = fakeMarkdownParser.instance,
|
||||||
|
textPillsUtils = fakeTextPillsUtils.instance,
|
||||||
|
thumbnailExtractor = fakeThumbnailExtractor.instance,
|
||||||
|
waveformSanitizer = fakeWaveFormSanitizer.instance,
|
||||||
|
localEchoRepository = fakeLocalEchoRepository.instance,
|
||||||
|
permalinkFactory = fakePermalinkFactory.instance,
|
||||||
|
clock = fakeClock
|
||||||
|
)
|
||||||
|
|
||||||
|
@Before
|
||||||
|
fun setup() {
|
||||||
|
fakeClock.givenEpoch(AN_EPOCH)
|
||||||
|
fakeMarkdownParser.givenBoldMarkdown()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `given a null quotedText, when a quote event is created, then the result message should only contain the new text after new lines`() {
|
||||||
|
val event = createTimelineEvent(null, null)
|
||||||
|
val quotedContent = localEchoEventFactory.createQuotedTextEvent(
|
||||||
|
roomId = A_ROOM_ID,
|
||||||
|
quotedEvent = event,
|
||||||
|
text = "Text",
|
||||||
|
formattedText = null,
|
||||||
|
autoMarkdown = false,
|
||||||
|
rootThreadEventId = null,
|
||||||
|
additionalContent = null,
|
||||||
|
).content.toModel<MessageContent>()
|
||||||
|
assertEquals("\n\nText", quotedContent?.body)
|
||||||
|
assertEquals("<br/>Text", (quotedContent as? MessageContentWithFormattedBody)?.formattedBody)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `given a plain text quoted message, when a quote event is created, then the result message should contain both the quoted and new text`() {
|
||||||
|
val event = createTimelineEvent("Quoted", null)
|
||||||
|
val quotedContent = localEchoEventFactory.createQuotedTextEvent(
|
||||||
|
roomId = A_ROOM_ID,
|
||||||
|
quotedEvent = event,
|
||||||
|
text = "Text",
|
||||||
|
formattedText = null,
|
||||||
|
autoMarkdown = false,
|
||||||
|
rootThreadEventId = null,
|
||||||
|
additionalContent = null,
|
||||||
|
).content.toModel<MessageContent>()
|
||||||
|
assertEquals("> Quoted\n\nText", quotedContent?.body)
|
||||||
|
assertEquals("<blockquote>Quoted</blockquote><br/>Text", (quotedContent as? MessageContentWithFormattedBody)?.formattedBody)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `given a formatted text quoted message, when a quote event is created, then the result message should contain both the formatted quote and new text`() {
|
||||||
|
val event = createTimelineEvent("Quoted", "<b>Quoted</b>")
|
||||||
|
val quotedContent = localEchoEventFactory.createQuotedTextEvent(
|
||||||
|
roomId = A_ROOM_ID,
|
||||||
|
quotedEvent = event,
|
||||||
|
text = "Text",
|
||||||
|
formattedText = null,
|
||||||
|
autoMarkdown = false,
|
||||||
|
rootThreadEventId = null,
|
||||||
|
additionalContent = null,
|
||||||
|
).content.toModel<MessageContent>()
|
||||||
|
// This still uses the plain text version
|
||||||
|
assertEquals("> Quoted\n\nText", quotedContent?.body)
|
||||||
|
// This one has the formatted one
|
||||||
|
assertEquals("<blockquote><b>Quoted</b></blockquote><br/>Text", (quotedContent as? MessageContentWithFormattedBody)?.formattedBody)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `given formatted text quoted message and new message, when a quote event is created, then the result message should contain both the formatted quote and new formatted text`() {
|
||||||
|
val event = createTimelineEvent("Quoted", "<b>Quoted</b>")
|
||||||
|
val quotedContent = localEchoEventFactory.createQuotedTextEvent(
|
||||||
|
roomId = A_ROOM_ID,
|
||||||
|
quotedEvent = event,
|
||||||
|
text = "Text",
|
||||||
|
formattedText = "<b>Formatted text</b>",
|
||||||
|
autoMarkdown = false,
|
||||||
|
rootThreadEventId = null,
|
||||||
|
additionalContent = null,
|
||||||
|
).content.toModel<MessageContent>()
|
||||||
|
// This still uses the plain text version
|
||||||
|
assertEquals("> Quoted\n\nText", quotedContent?.body)
|
||||||
|
// This one has the formatted one
|
||||||
|
assertEquals(
|
||||||
|
"<blockquote><b>Quoted</b></blockquote><br/><b>Formatted text</b>",
|
||||||
|
(quotedContent as? MessageContentWithFormattedBody)?.formattedBody
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `given formatted text quoted message and new message with autoMarkdown, when a quote event is created, then the result message should contain both the formatted quote and new formatted text, not the markdown processed text`() {
|
||||||
|
val event = createTimelineEvent("Quoted", "<b>Quoted</b>")
|
||||||
|
val quotedContent = localEchoEventFactory.createQuotedTextEvent(
|
||||||
|
roomId = A_ROOM_ID,
|
||||||
|
quotedEvent = event,
|
||||||
|
text = "Text",
|
||||||
|
formattedText = "<b>Formatted text</b>",
|
||||||
|
autoMarkdown = true,
|
||||||
|
rootThreadEventId = null,
|
||||||
|
additionalContent = null,
|
||||||
|
).content.toModel<MessageContent>()
|
||||||
|
// This still uses the plain text version
|
||||||
|
assertEquals("> Quoted\n\nText", quotedContent?.body)
|
||||||
|
// This one has the formatted one
|
||||||
|
assertEquals(
|
||||||
|
"<blockquote><b>Quoted</b></blockquote><br/><b>Formatted text</b>",
|
||||||
|
(quotedContent as? MessageContentWithFormattedBody)?.formattedBody
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `given a formatted text quoted message and a new message with autoMarkdown, when a quote event is created, then the result message should contain both the formatted quote and new processed formatted text`() {
|
||||||
|
val event = createTimelineEvent("Quoted", "<b>Quoted</b>")
|
||||||
|
val quotedContent = localEchoEventFactory.createQuotedTextEvent(
|
||||||
|
roomId = A_ROOM_ID,
|
||||||
|
quotedEvent = event,
|
||||||
|
text = "**Text**",
|
||||||
|
formattedText = null,
|
||||||
|
autoMarkdown = true,
|
||||||
|
rootThreadEventId = null,
|
||||||
|
additionalContent = null,
|
||||||
|
).content.toModel<MessageContent>()
|
||||||
|
// This still uses the markdown text version
|
||||||
|
assertEquals("> Quoted\n\n**Text**", quotedContent?.body)
|
||||||
|
// This one has the formatted one
|
||||||
|
assertEquals(
|
||||||
|
"<blockquote><b>Quoted</b></blockquote><br/><b>Text</b>",
|
||||||
|
(quotedContent as? MessageContentWithFormattedBody)?.formattedBody
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `given a plain text quoted message and a new message with autoMarkdown, when a quote event is created, then the result message should the plain text quote and new processed formatted text`() {
|
||||||
|
val event = createTimelineEvent("Quoted", null)
|
||||||
|
val quotedContent = localEchoEventFactory.createQuotedTextEvent(
|
||||||
|
roomId = A_ROOM_ID,
|
||||||
|
quotedEvent = event,
|
||||||
|
text = "**Text**",
|
||||||
|
formattedText = null,
|
||||||
|
autoMarkdown = true,
|
||||||
|
rootThreadEventId = null,
|
||||||
|
additionalContent = null,
|
||||||
|
).content.toModel<MessageContent>()
|
||||||
|
// This still uses the markdown text version
|
||||||
|
assertEquals("> Quoted\n\n**Text**", quotedContent?.body)
|
||||||
|
// This one has the formatted one
|
||||||
|
assertEquals(
|
||||||
|
"<blockquote>Quoted</blockquote><br/><b>Text</b>",
|
||||||
|
(quotedContent as? MessageContentWithFormattedBody)?.formattedBody
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createTimelineEvent(quotedText: String?, formattedQuotedText: String?): TimelineEvent {
|
||||||
|
val textContent = quotedText?.let {
|
||||||
|
TextContent(
|
||||||
|
quotedText,
|
||||||
|
formattedQuotedText
|
||||||
|
).toMessageTextContent().toContent()
|
||||||
|
}
|
||||||
|
return TimelineEvent(
|
||||||
|
root = A_START_EVENT,
|
||||||
|
localId = 1234,
|
||||||
|
eventId = AN_EVENT_ID,
|
||||||
|
displayIndex = 0,
|
||||||
|
senderInfo = SenderInfo(A_USER_ID_1, A_USER_ID_1, true, null),
|
||||||
|
annotations = if (textContent != null) {
|
||||||
|
EventAnnotationsSummary(
|
||||||
|
editSummary = EditAggregatedSummary(latestContent = textContent, emptyList(), emptyList())
|
||||||
|
)
|
||||||
|
} else null
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,37 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2022 The Matrix.org Foundation C.I.C.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.matrix.android.sdk.test.fakes
|
||||||
|
|
||||||
|
import android.content.ClipData
|
||||||
|
import android.content.ClipboardManager
|
||||||
|
import io.mockk.every
|
||||||
|
import io.mockk.just
|
||||||
|
import io.mockk.mockk
|
||||||
|
import io.mockk.runs
|
||||||
|
import io.mockk.verify
|
||||||
|
|
||||||
|
class FakeClipboardManager {
|
||||||
|
val instance = mockk<ClipboardManager>()
|
||||||
|
|
||||||
|
fun givenSetPrimaryClip() {
|
||||||
|
every { instance.setPrimaryClip(any()) } just runs
|
||||||
|
}
|
||||||
|
|
||||||
|
fun verifySetPrimaryClip(clipData: ClipData) {
|
||||||
|
verify { instance.setPrimaryClip(clipData) }
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,44 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2022 The Matrix.org Foundation C.I.C
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.matrix.android.sdk.test.fakes
|
||||||
|
|
||||||
|
import android.net.ConnectivityManager
|
||||||
|
import android.net.Network
|
||||||
|
import android.net.NetworkCapabilities
|
||||||
|
import io.mockk.every
|
||||||
|
import io.mockk.mockk
|
||||||
|
|
||||||
|
class FakeConnectivityManager {
|
||||||
|
val instance = mockk<ConnectivityManager>()
|
||||||
|
|
||||||
|
fun givenNoActiveConnection() {
|
||||||
|
every { instance.activeNetwork } returns null
|
||||||
|
}
|
||||||
|
|
||||||
|
fun givenHasActiveConnection() {
|
||||||
|
val network = mockk<Network>()
|
||||||
|
every { instance.activeNetwork } returns network
|
||||||
|
|
||||||
|
val networkCapabilities = FakeNetworkCapabilities()
|
||||||
|
networkCapabilities.givenTransports(
|
||||||
|
NetworkCapabilities.TRANSPORT_CELLULAR,
|
||||||
|
NetworkCapabilities.TRANSPORT_WIFI,
|
||||||
|
NetworkCapabilities.TRANSPORT_VPN
|
||||||
|
)
|
||||||
|
every { instance.getNetworkCapabilities(network) } returns networkCapabilities.instance
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,84 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2022 The Matrix.org Foundation C.I.C.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.matrix.android.sdk.test.fakes
|
||||||
|
|
||||||
|
import android.content.ClipboardManager
|
||||||
|
import android.content.ContentResolver
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import android.net.ConnectivityManager
|
||||||
|
import android.net.Uri
|
||||||
|
import android.os.ParcelFileDescriptor
|
||||||
|
import io.mockk.every
|
||||||
|
import io.mockk.just
|
||||||
|
import io.mockk.mockk
|
||||||
|
import io.mockk.runs
|
||||||
|
import java.io.OutputStream
|
||||||
|
|
||||||
|
class FakeContext(
|
||||||
|
private val contentResolver: ContentResolver = mockk()
|
||||||
|
) {
|
||||||
|
|
||||||
|
val instance = mockk<Context>()
|
||||||
|
|
||||||
|
init {
|
||||||
|
every { instance.contentResolver } returns contentResolver
|
||||||
|
every { instance.applicationContext } returns instance
|
||||||
|
}
|
||||||
|
|
||||||
|
fun givenFileDescriptor(uri: Uri, mode: String, factory: () -> ParcelFileDescriptor?) {
|
||||||
|
val fileDescriptor = factory()
|
||||||
|
every { contentResolver.openFileDescriptor(uri, mode, null) } returns fileDescriptor
|
||||||
|
}
|
||||||
|
|
||||||
|
fun givenSafeOutputStreamFor(uri: Uri): OutputStream {
|
||||||
|
val outputStream = mockk<OutputStream>(relaxed = true)
|
||||||
|
every { contentResolver.openOutputStream(uri, "wt") } returns outputStream
|
||||||
|
return outputStream
|
||||||
|
}
|
||||||
|
|
||||||
|
fun givenMissingSafeOutputStreamFor(uri: Uri) {
|
||||||
|
every { contentResolver.openOutputStream(uri, "wt") } returns null
|
||||||
|
}
|
||||||
|
|
||||||
|
fun givenNoConnection() {
|
||||||
|
val connectivityManager = FakeConnectivityManager()
|
||||||
|
connectivityManager.givenNoActiveConnection()
|
||||||
|
givenService(Context.CONNECTIVITY_SERVICE, ConnectivityManager::class.java, connectivityManager.instance)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun <T> givenService(name: String, klass: Class<T>, service: T) {
|
||||||
|
every { instance.getSystemService(name) } returns service
|
||||||
|
every { instance.getSystemService(klass) } returns service
|
||||||
|
}
|
||||||
|
|
||||||
|
fun givenHasConnection() {
|
||||||
|
val connectivityManager = FakeConnectivityManager()
|
||||||
|
connectivityManager.givenHasActiveConnection()
|
||||||
|
givenService(Context.CONNECTIVITY_SERVICE, ConnectivityManager::class.java, connectivityManager.instance)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun givenStartActivity(intent: Intent) {
|
||||||
|
every { instance.startActivity(intent) } just runs
|
||||||
|
}
|
||||||
|
|
||||||
|
fun givenClipboardManager(): FakeClipboardManager {
|
||||||
|
val fakeClipboardManager = FakeClipboardManager()
|
||||||
|
givenService(Context.CLIPBOARD_SERVICE, ClipboardManager::class.java, fakeClipboardManager.instance)
|
||||||
|
return fakeClipboardManager
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,32 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2022 The Matrix.org Foundation C.I.C.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.matrix.android.sdk.test.fakes
|
||||||
|
|
||||||
|
import android.net.NetworkCapabilities
|
||||||
|
import io.mockk.every
|
||||||
|
import io.mockk.mockk
|
||||||
|
|
||||||
|
class FakeNetworkCapabilities {
|
||||||
|
val instance = mockk<NetworkCapabilities>()
|
||||||
|
|
||||||
|
fun givenTransports(vararg type: Int) {
|
||||||
|
every { instance.hasTransport(any()) } answers {
|
||||||
|
val input = it.invocation.args.first() as Int
|
||||||
|
type.contains(input)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,24 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2022 The Matrix.org Foundation C.I.C.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.matrix.android.sdk.test.fakes.internal.session.content
|
||||||
|
|
||||||
|
import io.mockk.mockk
|
||||||
|
import org.matrix.android.sdk.internal.session.content.ThumbnailExtractor
|
||||||
|
|
||||||
|
class FakeThumbnailExtractor {
|
||||||
|
internal val instance = mockk<ThumbnailExtractor>()
|
||||||
|
}
|
||||||
@ -0,0 +1,24 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2022 The Matrix.org Foundation C.I.C.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.matrix.android.sdk.test.fakes.internal.session.permalinks
|
||||||
|
|
||||||
|
import io.mockk.mockk
|
||||||
|
import org.matrix.android.sdk.internal.session.permalinks.PermalinkFactory
|
||||||
|
|
||||||
|
class FakePermalinkFactory {
|
||||||
|
internal val instance = mockk<PermalinkFactory>()
|
||||||
|
}
|
||||||
@ -0,0 +1,24 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2022 The Matrix.org Foundation C.I.C.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.matrix.android.sdk.test.fakes.internal.session.room.send
|
||||||
|
|
||||||
|
import io.mockk.mockk
|
||||||
|
import org.matrix.android.sdk.internal.session.room.send.LocalEchoRepository
|
||||||
|
|
||||||
|
class FakeLocalEchoRepository {
|
||||||
|
internal val instance = mockk<LocalEchoRepository>()
|
||||||
|
}
|
||||||
@ -0,0 +1,32 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2022 The Matrix.org Foundation C.I.C.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.matrix.android.sdk.test.fakes.internal.session.room.send
|
||||||
|
|
||||||
|
import io.mockk.every
|
||||||
|
import io.mockk.mockk
|
||||||
|
import org.matrix.android.sdk.api.util.TextContent
|
||||||
|
import org.matrix.android.sdk.internal.session.room.send.MarkdownParser
|
||||||
|
|
||||||
|
class FakeMarkdownParser {
|
||||||
|
internal val instance = mockk<MarkdownParser>()
|
||||||
|
fun givenBoldMarkdown() {
|
||||||
|
every { instance.parse(any(), any(), any()) } answers {
|
||||||
|
val text = arg<String>(0)
|
||||||
|
TextContent(text, "<b>${text.replace("*", "")}</b>")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,24 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2022 The Matrix.org Foundation C.I.C.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.matrix.android.sdk.test.fakes.internal.session.room.send
|
||||||
|
|
||||||
|
import io.mockk.mockk
|
||||||
|
import org.matrix.android.sdk.internal.session.room.send.WaveFormSanitizer
|
||||||
|
|
||||||
|
class FakeWaveFormSanitizer {
|
||||||
|
internal val instance = mockk<WaveFormSanitizer>()
|
||||||
|
}
|
||||||
@ -0,0 +1,24 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2022 The Matrix.org Foundation C.I.C.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.matrix.android.sdk.test.fakes.internal.session.room.send.pills
|
||||||
|
|
||||||
|
import io.mockk.mockk
|
||||||
|
import org.matrix.android.sdk.internal.session.room.send.pills.TextPillsUtils
|
||||||
|
|
||||||
|
class FakeTextPillsUtils {
|
||||||
|
internal val instance = mockk<TextPillsUtils>()
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user