[gitlab] IJPL-82619: "/uploads/*" links are rendered correctly and targets correct gitlab-based urls

* works for comments in the editor, MR timeline notes, MR title, and MR diff comments, and also for title/details of commits in Gitlab toolwindow
* doesn't work for the titles in the list of MRs in Gitlab toolwindow

GitOrigin-RevId: 7a19d6520edb6f13077fa32f9d5ce7ae8c545834
This commit is contained in:
Valeria Golovina
2025-10-29 22:58:26 +00:00
committed by intellij-monorepo-bot
parent 526ad7da93
commit fdd225e48e
14 changed files with 117 additions and 41 deletions
@@ -1,4 +1,5 @@
fragment project on Project {
id
name
nameWithNamespace
path
@@ -1,15 +1,20 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package org.jetbrains.plugins.gitlab.api.dto
import com.fasterxml.jackson.annotation.JsonIgnore
import com.fasterxml.jackson.annotation.JsonProperty
import com.intellij.collaboration.api.dto.GraphQLFragment
import com.intellij.openapi.util.NlsSafe
import org.jetbrains.annotations.Nls
import org.jetbrains.plugins.gitlab.api.GitLabGid
import org.jetbrains.plugins.gitlab.api.GitLabGidData
import org.jetbrains.plugins.gitlab.api.SinceGitLab
// namespace can be null for some reason, so we need to parse paths manually
@SinceGitLab("12.0")
@GraphQLFragment("/graphql/fragment/project.graphql")
data class GitLabProjectDTO(
@JsonProperty("id") private val _id: String,
val name: @Nls String,
val nameWithNamespace: @Nls String,
val path: @NlsSafe String,
@@ -24,6 +29,7 @@ data class GitLabProjectDTO(
@SinceGitLab("16.8") val allowsMultipleMergeRequestReviewers: Boolean?,
val repository: Repository?
) {
@JsonIgnore val id: GitLabGid = GitLabGidData(_id)
val ownerPath: @NlsSafe String = fullPath.split("/").dropLast(1).joinToString("/")
data class Repository(
@@ -20,9 +20,11 @@ import org.jetbrains.plugins.gitlab.api.request.*
import org.jetbrains.plugins.gitlab.mergerequest.api.dto.GitLabMergeRequestDTO
import org.jetbrains.plugins.gitlab.mergerequest.api.request.loadMergeRequest
import org.jetbrains.plugins.gitlab.mergerequest.api.request.mergeRequestSetReviewers
import org.jetbrains.plugins.gitlab.mergerequest.ui.GitLabContextDataLoader
import org.jetbrains.plugins.gitlab.util.GitLabProjectMapping
import org.jetbrains.plugins.gitlab.util.GitLabRegistry
private val LOG = logger<GitLabProject>()
@CodeReviewDomainEntity
@@ -38,6 +40,7 @@ interface GitLabProject {
fun getMembersBatches(): Flow<List<GitLabUserDTO>>
val defaultBranch: String?
val gitLabProjectId: GitLabId
suspend fun isMultipleReviewersAllowed(): Boolean
/**
@@ -50,6 +53,8 @@ interface GitLabProject {
suspend fun adjustReviewers(mrIid: String, reviewers: List<GitLabUserDTO>): GitLabMergeRequestDTO
fun reloadData()
val contextDataLoader: GitLabContextDataLoader
}
@CodeReviewDomainEntity
@@ -77,6 +82,11 @@ class GitLabLazyProject(
private val multipleReviewersAllowedRequest = cs.async(Dispatchers.IO, start = CoroutineStart.LAZY) {
loadMultipleReviewersAllowed(initialData)
}
override val gitLabProjectId: GitLabId = initialData.id
val uploadFileUrlBase: String = projectMapping.repository.serverPath.toString() + "/-/project/" + gitLabProjectId.guessRestId() + "/uploads/"
override val contextDataLoader: GitLabContextDataLoader = GitLabContextDataLoader(uploadFileUrlBase)
override val mergeRequests by lazy {
CachingGitLabProjectMergeRequestsStore(project, cs, api, glMetadata, projectMapping, currentUser, tokenRefreshFlow)
@@ -0,0 +1,12 @@
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package org.jetbrains.plugins.gitlab.mergerequest.ui
import org.jetbrains.annotations.ApiStatus
/**
* Class which handles external files loading
*/
@ApiStatus.Internal
class GitLabContextDataLoader(
val uploadFileUrlBase: String
)
@@ -5,13 +5,16 @@ import com.intellij.openapi.project.Project
import org.jetbrains.annotations.ApiStatus
import org.jetbrains.plugins.gitlab.mergerequest.data.GitLabCommit
import org.jetbrains.plugins.gitlab.mergerequest.data.GitLabMergeRequest
import org.jetbrains.plugins.gitlab.mergerequest.ui.GitLabContextDataLoader
import org.jetbrains.plugins.gitlab.ui.GitLabUIUtil
@ApiStatus.Internal
class GitLabCommitViewModel(
private val project: Project,
private val mr: GitLabMergeRequest,
model: GitLabCommit
model: GitLabCommit,
private val contextDataLoader: GitLabContextDataLoader
) {
internal val sha = model.sha
internal val shortId = model.shortId
@@ -20,9 +23,9 @@ class GitLabCommitViewModel(
internal val authoredDate = model.authoredDate
internal val titleHtml = model.fullTitle?.let {
GitLabUIUtil.convertToHtml(project, mr.gitRepository, mr.glProject.projectPath, it)
GitLabUIUtil.convertToHtml(project, mr.gitRepository, mr.glProject.projectPath, it, contextDataLoader.uploadFileUrlBase)
}
internal val descriptionHtml = model.description?.removePrefix(model.fullTitle.orEmpty())?.let {
GitLabUIUtil.convertToHtml(project, mr.gitRepository, mr.glProject.projectPath, it)
GitLabUIUtil.convertToHtml(project, mr.gitRepository, mr.glProject.projectPath, it, contextDataLoader.uploadFileUrlBase)
}
}
@@ -18,6 +18,7 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.*
import org.jetbrains.annotations.ApiStatus
import org.jetbrains.plugins.gitlab.mergerequest.data.GitLabMergeRequest
import org.jetbrains.plugins.gitlab.mergerequest.ui.GitLabContextDataLoader
@ApiStatus.Internal
interface GitLabMergeRequestChangesViewModel : CodeReviewChangesViewModel<GitLabCommitViewModel> {
@@ -34,7 +35,8 @@ private val LOG = logger<GitLabMergeRequestChangesViewModel>()
internal class GitLabMergeRequestChangesViewModelImpl(
private val project: Project,
parentCs: CoroutineScope,
mergeRequest: GitLabMergeRequest
mergeRequest: GitLabMergeRequest,
private val contextDataLoader: GitLabContextDataLoader,
) : GitLabMergeRequestChangesViewModel,
CodeReviewChangesViewModel<GitLabCommitViewModel> {
private val cs = parentCs.childScope(javaClass.name)
@@ -54,7 +56,7 @@ internal class GitLabMergeRequestChangesViewModelImpl(
override val reviewCommits: SharedFlow<List<GitLabCommitViewModel>> =
mergeRequest.changes
.map { it.getCommits().map { commit -> GitLabCommitViewModel(project, mergeRequest, commit) } }
.map { it.getCommits().map { commit -> GitLabCommitViewModel(project, mergeRequest, commit, contextDataLoader) } }
.modelFlow(cs, LOG)
override val selectedCommitIndex: SharedFlow<Int> = reviewCommits.combine(delegate.selectedCommit) { commits, sha ->
@@ -56,13 +56,15 @@ internal class GitLabMergeRequestDetailsViewModelImpl(
override val author: GitLabUserDTO = mergeRequest.author
override val title: SharedFlow<String> = mergeRequest.details.map { it.title }.map { title ->
GitLabUIUtil.convertToHtml(project, mergeRequest.gitRepository, mergeRequest.glProject.projectPath, title)
GitLabUIUtil.convertToHtml(project, mergeRequest.gitRepository, mergeRequest.glProject.projectPath, title,
projectData.contextDataLoader.uploadFileUrlBase)
}.modelFlow(cs, LOG)
override val description: SharedFlow<String> = mergeRequest.details.map { it.description }.map { description ->
processIssueIdsHtml(project, description)
}.modelFlow(cs, LOG)
override val descriptionHtml: SharedFlow<String> = mergeRequest.details.map { it.description }.map {
if (it.isNotBlank()) GitLabUIUtil.convertToHtml(project, mergeRequest.gitRepository, mergeRequest.glProject.projectPath, it) else it
if (it.isNotBlank()) GitLabUIUtil.convertToHtml(project, mergeRequest.gitRepository, mergeRequest.glProject.projectPath, it,
projectData.contextDataLoader.uploadFileUrlBase) else it
}.modelFlow(cs, LOG)
override val reviewRequestState: SharedFlow<ReviewRequestState> = mergeRequest.details.map { it.reviewState }
.modelFlow(cs, LOG)
@@ -84,7 +86,7 @@ internal class GitLabMergeRequestDetailsViewModelImpl(
override val branchesVm = GitLabMergeRequestBranchesViewModel(cs, mergeRequest, projectData.projectMapping)
override val statusVm = GitLabMergeRequestStatusViewModelImpl(project, cs, projectData.projectMapping.gitRepository,
projectData.projectMapping.repository.serverPath, mergeRequest)
override val changesVm = GitLabMergeRequestChangesViewModelImpl(project, cs, mergeRequest)
override val changesVm = GitLabMergeRequestChangesViewModelImpl(project, cs, mergeRequest, projectData.contextDataLoader)
override fun reloadData() {
cs.launchNow {
@@ -88,7 +88,8 @@ internal class GitLabMergeRequestDiscussionsViewModelsImpl(
.map { ComputedResult.fromResult(it) }
.transformConsecutiveSuccesses {
mapFiltered { it.discussionId == null }
.mapStatefulToStateful { GitLabMergeRequestStandaloneDraftNoteViewModelBase(project, this, it, mergeRequest) }
.mapStatefulToStateful { GitLabMergeRequestStandaloneDraftNoteViewModelBase(project, this, it, mergeRequest,
projectData.contextDataLoader) }
}
.stateInNow(cs, ComputedResult.loading())
@@ -26,14 +26,15 @@ sealed interface GitLabMergeRequestTimelineItemViewModel {
sealed class Immutable(
private val project: Project,
private val mr: GitLabMergeRequest,
private val model: GitLabMergeRequestTimelineItem.Immutable
private val model: GitLabMergeRequestTimelineItem.Immutable,
private val uploadFileUrlBase: String,
) : GitLabMergeRequestTimelineItemViewModel {
val date = model.date
val actor = model.actor
val contentHtml: String? by lazy {
(model as? GitLabMergeRequestTimelineItem.SystemNote)?.content?.let {
GitLabUIUtil.convertToHtml(project, mr.gitRepository, mr.glProject.projectPath, it)
GitLabUIUtil.convertToHtml(project, mr.gitRepository, mr.glProject.projectPath, it, uploadFileUrlBase)
}
}
@@ -41,33 +42,41 @@ sealed interface GitLabMergeRequestTimelineItemViewModel {
fun fromModel(
project: Project,
mr: GitLabMergeRequest,
model: GitLabMergeRequestTimelineItem.Immutable
): Immutable = when (model) {
is GitLabMergeRequestTimelineItem.StateEvent -> StateEvent(project, mr, model)
is GitLabMergeRequestTimelineItem.LabelEvent -> LabelEvent(project, mr, model)
is GitLabMergeRequestTimelineItem.MilestoneEvent -> MilestoneEvent(project, mr, model)
is GitLabMergeRequestTimelineItem.SystemNote -> SystemNote(project, mr, model)
model: GitLabMergeRequestTimelineItem.Immutable,
uploadFileUrlBase: String,
): Immutable {
return when (model) {
is GitLabMergeRequestTimelineItem.StateEvent -> StateEvent(project, mr, model, uploadFileUrlBase)
is GitLabMergeRequestTimelineItem.LabelEvent -> LabelEvent(project, mr, model, uploadFileUrlBase)
is GitLabMergeRequestTimelineItem.MilestoneEvent -> MilestoneEvent(project, mr, model, uploadFileUrlBase)
is GitLabMergeRequestTimelineItem.SystemNote -> SystemNote(project, mr, model, uploadFileUrlBase)
}
}
}
}
class StateEvent(project: Project, mr: GitLabMergeRequest, model: GitLabMergeRequestTimelineItem.StateEvent)
: Immutable(project, mr, model) {
class StateEvent(project: Project, mr: GitLabMergeRequest, model: GitLabMergeRequestTimelineItem.StateEvent,
uploadFileUrlBase: String)
: Immutable(project, mr, model, uploadFileUrlBase) {
val event = model.event
}
class LabelEvent(project: Project, mr: GitLabMergeRequest, model: GitLabMergeRequestTimelineItem.LabelEvent)
: Immutable(project, mr, model) {
class LabelEvent(project: Project, mr: GitLabMergeRequest, model: GitLabMergeRequestTimelineItem.LabelEvent,
uploadFileUrlBase: String)
: Immutable(project, mr, model, uploadFileUrlBase) {
val event = model.event
}
class MilestoneEvent(project: Project, mr: GitLabMergeRequest, model: GitLabMergeRequestTimelineItem.MilestoneEvent)
: Immutable(project, mr, model) {
class MilestoneEvent(project: Project, mr: GitLabMergeRequest, model: GitLabMergeRequestTimelineItem.MilestoneEvent,
uploadFileUrlBase: String)
: Immutable(project, mr, model, uploadFileUrlBase) {
val event = model.event
}
class SystemNote(project: Project, mr: GitLabMergeRequest, model: GitLabMergeRequestTimelineItem.SystemNote)
: Immutable(project, mr, model) {
class SystemNote(project: Project, mr: GitLabMergeRequest, model: GitLabMergeRequestTimelineItem.SystemNote,
uploadFileUrlBase: String)
: Immutable(project, mr, model, uploadFileUrlBase) {
val content = model.content
}
@@ -97,7 +106,8 @@ sealed interface GitLabMergeRequestTimelineItemViewModel {
project: Project,
parentCs: CoroutineScope,
mr: GitLabMergeRequest,
note: GitLabMergeRequestNote
note: GitLabMergeRequestNote,
projectData: GitLabProject
) : GitLabMergeRequestTimelineItemViewModel, GitLabNoteViewModel {
private val cs = parentCs.childScope(this::class)
@@ -113,7 +123,7 @@ sealed interface GitLabMergeRequestTimelineItemViewModel {
override val body: StateFlow<String> = note.body
override val bodyHtml: StateFlow<String> = body.mapStateInNow(cs) {
GitLabUIUtil.convertToHtml(project, mr.gitRepository, mr.glProject.projectPath, it)
GitLabUIUtil.convertToHtml(project, mr.gitRepository, mr.glProject.projectPath, it, projectData.contextDataLoader.uploadFileUrlBase)
}
override val discussionState: StateFlow<GitLabDiscussionStateContainer> =
@@ -47,10 +47,12 @@ internal class LoadAllGitLabMergeRequestTimelineViewModel(
override val number: String = "!${mergeRequest.iid}"
override val author: GitLabUserDTO = mergeRequest.author
override val title: SharedFlow<String> = mergeRequest.details.map { it.title }.map { title ->
GitLabUIUtil.convertToHtml(project, mergeRequest.gitRepository, mergeRequest.glProject.projectPath, title)
GitLabUIUtil.convertToHtml(project, mergeRequest.gitRepository, mergeRequest.glProject.projectPath, title,
projectData.contextDataLoader.uploadFileUrlBase)
}.modelFlow(cs, LOG)
override val descriptionHtml: SharedFlow<String> = mergeRequest.details.map { it.description }.map { description ->
GitLabUIUtil.convertToHtml(project, mergeRequest.gitRepository, mergeRequest.glProject.projectPath, description)
GitLabUIUtil.convertToHtml(project, mergeRequest.gitRepository, mergeRequest.glProject.projectPath, description,
projectData.contextDataLoader.uploadFileUrlBase)
}.modelFlow(cs, LOG)
override val url: String = mergeRequest.url
@@ -60,7 +62,7 @@ internal class LoadAllGitLabMergeRequestTimelineViewModel(
override val timelineItems: SharedFlow<Result<List<GitLabMergeRequestTimelineItemViewModel>>> =
mergeRequest.createTimelineItemsFlow(showEvents)
.transformConsecutiveSuccesses {
mapDataToModel({ it.id }, { createItemVm(it) }, {})
mapDataToModel({ it.id }, { createItemVm(it, projectData.contextDataLoader.uploadFileUrlBase) }, {})
}
.modelFlow(cs, LOG)
@@ -142,17 +144,17 @@ internal class LoadAllGitLabMergeRequestTimelineViewModel(
}
}
private fun CoroutineScope.createItemVm(item: GitLabMergeRequestTimelineItem)
private fun CoroutineScope.createItemVm(item: GitLabMergeRequestTimelineItem, basePath: String)
: GitLabMergeRequestTimelineItemViewModel =
when (item) {
is GitLabMergeRequestTimelineItem.Immutable ->
GitLabMergeRequestTimelineItemViewModel.Immutable.fromModel(project, mergeRequest, item)
GitLabMergeRequestTimelineItemViewModel.Immutable.fromModel(project, mergeRequest, item, basePath)
is GitLabMergeRequestTimelineItem.UserDiscussion ->
GitLabMergeRequestTimelineItemViewModel.Discussion(project, cs, projectData, currentUser, mergeRequest, item.discussion).also {
handleDiffRequests(it.diffVm, _diffRequests::emit)
}
is GitLabMergeRequestTimelineItem.DraftNote ->
GitLabMergeRequestTimelineItemViewModel.DraftNote(project, cs, mergeRequest, item.note).also {
GitLabMergeRequestTimelineItemViewModel.DraftNote(project, cs, mergeRequest, item.note, projectData).also {
handleDiffRequests(it.diffVm, _diffRequests::emit)
}
}
@@ -47,6 +47,8 @@ import java.net.URI
private val MARKDOWN_IMAGE_SETTINGS: IElementType = MarkdownElementType("MARKDOWN_IMAGE_SETTINGS")
private const val UPLOADS_PATH = "/uploads/"
object GitLabUIUtil {
const val OPEN_FILE_LINK_PREFIX = "glfilelink:"
const val OPEN_MR_LINK_PREFIX = "glmergerequest:"
@@ -64,12 +66,14 @@ object GitLabUIUtil {
project: Project,
gitRepository: GitRepository,
projectPath: GitLabProjectPath,
markdownSource: @NonNls String
markdownSource: @NonNls String,
uploadFileUrlBase: String
): @NlsSafe String {
if (markdownSource.isBlank()) return markdownSource
// TODO: fix bug with CRLF line endings from markdown library
val text = preprocessMergeRequestIds(processIssueIdsMarkdown(project, markdownSource)).replace("\r", "")
val flavourDescriptor = GitLabFlavourDescriptor(gitRepository, projectPath, CodeBlockHtmlSyntaxHighlighter(project))
val flavourDescriptor = GitLabFlavourDescriptor(gitRepository, projectPath, CodeBlockHtmlSyntaxHighlighter(project),
uploadFileUrlBase)
return MarkdownToHtmlConverter(flavourDescriptor).convertMarkdownToHtml(text, null)
}
@@ -80,7 +84,8 @@ object GitLabUIUtil {
private class GitLabFlavourDescriptor(
private val gitRepository: GitRepository,
private val projectPath: GitLabProjectPath,
private val htmlSyntaxHighlighter: HtmlSyntaxHighlighter
private val htmlSyntaxHighlighter: HtmlSyntaxHighlighter,
private val uploadFileUrlBase: String,
) : GFMFlavourDescriptor() {
/**
@@ -104,7 +109,7 @@ object GitLabUIUtil {
MarkdownElementTypes.IMAGE to GitLabImageWithSettingsGeneratingProvider(linkMap, baseURI).makeXssSafe(useSafeLinks),
GFMElementTypes.STRIKETHROUGH to SimpleInlineTagProvider("strike", 2, -2),
MarkdownElementTypes.CODE_FENCE to CodeFenceSyntaxHighlighterGeneratingProvider(htmlSyntaxHighlighter),
MarkdownElementTypes.INLINE_LINK to GitLabLinkGeneratingProvider(gitRepository, projectPath, map[MarkdownElementTypes.INLINE_LINK]),
MarkdownElementTypes.INLINE_LINK to GitLabLinkGeneratingProvider(gitRepository, projectPath, uploadFileUrlBase, map[MarkdownElementTypes.INLINE_LINK]),
)
}
}
@@ -198,6 +203,7 @@ object GitLabUIUtil {
private class GitLabLinkGeneratingProvider(
private val gitRepository: GitRepository,
private val projectPath: GitLabProjectPath,
private val uploadFileUrlBase: String,
private val fallback: GeneratingProvider?
) : GeneratingProvider {
override fun processNode(visitor: HtmlGenerator.HtmlGeneratingVisitor, text: String, node: ASTNode) {
@@ -231,6 +237,13 @@ object GitLabUIUtil {
return
}
// "uploads" files links should be updated to absolute URLs of the uploads
if (linkDestination.startsWith(UPLOADS_PATH)) {
visitor.consumeHtml(createLink(
uploadFileUrlBase + linkDestination.substring(UPLOADS_PATH.length), linkText).toString())
return
}
// Otherwise, the destination is a file in the current git repo, so we can make the link go to it directly
try {
val fileDestination = gitRepository.root.toNioPath().resolve(linkDestination.replace('\\', '/')).toString()
@@ -16,6 +16,7 @@ import kotlinx.coroutines.flow.*
import org.jetbrains.plugins.gitlab.api.GitLabId
import org.jetbrains.plugins.gitlab.api.dto.GitLabUserDTO
import org.jetbrains.plugins.gitlab.mergerequest.data.*
import org.jetbrains.plugins.gitlab.mergerequest.ui.GitLabContextDataLoader
import org.jetbrains.plugins.gitlab.mergerequest.ui.emoji.GitLabReactionsViewModel
import org.jetbrains.plugins.gitlab.ui.GitLabUIUtil
import org.jetbrains.plugins.gitlab.ui.comment.GitLabMergeRequestDiscussionViewModel.NoteItem
@@ -116,6 +117,7 @@ class GitLabMergeRequestStandaloneDraftNoteViewModelBase internal constructor(
parentCs: CoroutineScope,
note: GitLabMergeRequestDraftNote,
mr: GitLabMergeRequest,
contextDataLoader: GitLabContextDataLoader,
) : GitLabNoteViewModel {
private val cs = parentCs.childScope(this::class)
@@ -132,7 +134,7 @@ class GitLabMergeRequestStandaloneDraftNoteViewModelBase internal constructor(
override val body: StateFlow<String> = note.body
override val bodyHtml: StateFlow<String> = body.mapStateInNow(cs) {
GitLabUIUtil.convertToHtml(project, mr.gitRepository, mr.glProject.projectPath, it)
GitLabUIUtil.convertToHtml(project, mr.gitRepository, mr.glProject.projectPath, it, contextDataLoader.uploadFileUrlBase)
}
override val discussionState: StateFlow<GitLabDiscussionStateContainer> =
@@ -17,6 +17,7 @@ import org.jetbrains.annotations.Nls
import org.jetbrains.plugins.gitlab.api.GitLabId
import org.jetbrains.plugins.gitlab.api.dto.GitLabUserDTO
import org.jetbrains.plugins.gitlab.mergerequest.data.*
import org.jetbrains.plugins.gitlab.mergerequest.ui.GitLabContextDataLoader
import org.jetbrains.plugins.gitlab.mergerequest.ui.emoji.GitLabReactionsViewModel
import org.jetbrains.plugins.gitlab.mergerequest.ui.emoji.GitLabReactionsViewModelImpl
import org.jetbrains.plugins.gitlab.ui.GitLabUIUtil
@@ -64,7 +65,8 @@ class GitLabNoteViewModelImpl(
override val body: StateFlow<String> = note.body
override val bodyHtml: StateFlow<String> = body.mapStateInNow(cs) {
GitLabUIUtil.convertToHtml(project, projectData.projectMapping.gitRepository, projectData.projectMapping.repository.projectPath,it)
GitLabUIUtil.convertToHtml(project, projectData.projectMapping.gitRepository, projectData.projectMapping.repository.projectPath, it,
projectData.contextDataLoader.uploadFileUrlBase)
}
override val discussionState: StateFlow<GitLabDiscussionStateContainer> = isMainNote.map {
@@ -17,6 +17,8 @@ class GitLabUIUtilTest : LightPlatformTestCase() {
private lateinit var gitRepository: GitRepository
private lateinit var gitRootVf: VirtualFile
private val baseUrl = "http://base/url/uploads/"
override fun setUp() {
super.setUp()
@@ -36,7 +38,7 @@ class GitLabUIUtilTest : LightPlatformTestCase() {
GitLabUIUtil.convertToHtml(
project, gitRepository, GitLabProjectPath("test-account", "mr-test"), """
[link](/some/invalid/file/path?query=123)
""".trimIndent())
""".trimIndent(), baseUrl)
}
}
@@ -72,6 +74,14 @@ class GitLabUIUtilTest : LightPlatformTestCase() {
assertThat(parsed.substituteSeparators()).contains("${GitLabUIUtil.OPEN_FILE_LINK_PREFIX}${gitRoot}/directory/a/b/bla.md")
}
fun `test uploads files link rendering`() {
val parsed = convertToHtml("""
[link](/uploads/a/b/c.jpg) some text
""".trimIndent())
assertThat(parsed).isEqualTo("""<body><p><a href="${baseUrl}a/b/c.jpg">link</a> some text</p></body>""")
}
fun `test simple MR link gets MR link prefix`() {
val parsed = convertToHtml("""
!53
@@ -240,7 +250,7 @@ class GitLabUIUtilTest : LightPlatformTestCase() {
private fun convertToHtml(markdownSource: String): @NlsSafe String {
val parsed = GitLabUIUtil.convertToHtml(
project, gitRepository, GitLabProjectPath("test-account", "mr-test"), markdownSource)
project, gitRepository, GitLabProjectPath("test-account", "mr-test"), markdownSource, baseUrl)
return parsed
}
}