44package integration
55
66import (
7+ "fmt"
78 "net/http"
89 "net/http/httptest"
910 "net/url"
@@ -17,6 +18,7 @@ import (
1718 "code.gitea.io/gitea/models/unittest"
1819 user_model "code.gitea.io/gitea/models/user"
1920 "code.gitea.io/gitea/modules/git"
21+ "code.gitea.io/gitea/modules/gitrepo"
2022 "code.gitea.io/gitea/modules/test"
2123 issue_service "code.gitea.io/gitea/services/issue"
2224 repo_service "code.gitea.io/gitea/services/repository"
@@ -276,3 +278,253 @@ func testIssueClose(t *testing.T, session *TestSession, owner, repo, issueNumber
276278 req = NewRequestWithValues (t , "POST" , closeURL , options )
277279 return session .MakeRequest (t , req , http .StatusOK )
278280}
281+
282+ func Test_ReviewCodeComment (t * testing.T ) {
283+ onGiteaRun (t , func (t * testing.T , u * url.URL ) {
284+ user2 := unittest .AssertExistsAndLoadBean (t , & user_model.User {ID : 2 })
285+
286+ // Create the repo.
287+ repo , err := repo_service .CreateRepositoryDirectly (db .DefaultContext , user2 , user2 , repo_service.CreateRepoOptions {
288+ Name : "test_codecomment" ,
289+ Readme : "Default" ,
290+ AutoInit : true ,
291+ ObjectFormatName : git .Sha1ObjectFormat .Name (),
292+ DefaultBranch : "master" ,
293+ }, true )
294+ assert .NoError (t , err )
295+
296+ // add README.md to default branch
297+ _ , err = files_service .ChangeRepoFiles (db .DefaultContext , repo , user2 , & files_service.ChangeRepoFilesOptions {
298+ OldBranch : repo .DefaultBranch ,
299+ Files : []* files_service.ChangeRepoFile {
300+ {
301+ Operation : "update" ,
302+ TreePath : "README.md" ,
303+ ContentReader : strings .NewReader ("# 111\n # 222\n " ),
304+ },
305+ },
306+ })
307+ assert .NoError (t , err )
308+
309+ var pr * issues_model.PullRequest
310+ t .Run ("Create Pull Request" , func (t * testing.T ) {
311+ // create a new branch to prepare for pull request
312+ _ , err = files_service .ChangeRepoFiles (db .DefaultContext , repo , user2 , & files_service.ChangeRepoFilesOptions {
313+ NewBranch : "branch_codecomment1" ,
314+ Files : []* files_service.ChangeRepoFile {
315+ {
316+ Operation : "update" ,
317+ TreePath : "README.md" ,
318+ // add 5 lines to the file
319+ ContentReader : strings .NewReader ("# 111\n # 222\n # 333\n # 444\n # 555\n # 666\n # 777\n " ),
320+ },
321+ },
322+ })
323+ assert .NoError (t , err )
324+
325+ // Create a pull request.
326+ session := loginUser (t , "user2" )
327+ testPullCreate (t , session , "user2" , "test_codecomment" , false , repo .DefaultBranch , "branch_codecomment1" , "Test Pull Request1" )
328+
329+ pr = unittest .AssertExistsAndLoadBean (t , & issues_model.PullRequest {BaseRepoID : repo .ID , HeadBranch : "branch_codecomment1" })
330+ })
331+
332+ t .Run ("Create Code Comment" , func (t * testing.T ) {
333+ session := loginUser (t , "user2" )
334+
335+ // Grab the CSRF token.
336+ req := NewRequest (t , "GET" , path .Join ("user2" , "test_codecomment" , "pulls" , "1" ))
337+ resp := session .MakeRequest (t , req , http .StatusOK )
338+ htmlDoc := NewHTMLParser (t , resp .Body )
339+
340+ // Create a code comment on the pull request.
341+ commentURL := fmt .Sprintf ("/user2/test_codecomment/pulls/%d/files/reviews/comments" , pr .Index )
342+ options := map [string ]string {
343+ "_csrf" : htmlDoc .GetCSRF (),
344+ "origin" : "diff" ,
345+ "content" : "code comment on right line 4" ,
346+ "side" : "proposed" ,
347+ "line" : "4" ,
348+ "path" : "README.md" ,
349+ "single_review" : "true" ,
350+ "reply" : "0" ,
351+ "before_commit_id" : "" ,
352+ "after_commit_id" : "" ,
353+ }
354+ req = NewRequestWithValues (t , "POST" , commentURL , options )
355+ session .MakeRequest (t , req , http .StatusOK )
356+
357+ // Check if the comment was created.
358+ comment := unittest .AssertExistsAndLoadBean (t , & issues_model.Comment {
359+ Type : issues_model .CommentTypeCode ,
360+ IssueID : pr .IssueID ,
361+ })
362+ assert .Equal (t , "code comment on right line 4" , comment .Content )
363+ assert .Equal (t , "README.md" , comment .TreePath )
364+ assert .Equal (t , int64 (4 ), comment .Line )
365+ gitRepo , err := gitrepo .OpenRepository (t .Context (), repo )
366+ assert .NoError (t , err )
367+ defer gitRepo .Close ()
368+ commitID , err := gitRepo .GetRefCommitID (pr .GetGitHeadRefName ())
369+ assert .NoError (t , err )
370+ assert .Equal (t , commitID , comment .CommitSHA )
371+
372+ // load the files page and confirm the comment is there
373+ filesPageURL := fmt .Sprintf ("/user2/test_codecomment/pulls/%d/files" , pr .Index )
374+ req = NewRequest (t , "GET" , filesPageURL )
375+ resp = session .MakeRequest (t , req , http .StatusOK )
376+ htmlDoc = NewHTMLParser (t , resp .Body )
377+ commentHTML := htmlDoc .Find (fmt .Sprintf ("#issuecomment-%d" , comment .ID ))
378+ assert .NotNil (t , commentHTML )
379+ assert .Equal (t , "code comment on right line 4" , strings .TrimSpace (commentHTML .Find (".comment-body .render-content" ).Text ()))
380+
381+ // the last line of this comment line number is 4
382+ parentTr := commentHTML .ParentsFiltered ("tr" ).First ()
383+ assert .NotNil (t , parentTr )
384+ previousTr := parentTr .PrevAllFiltered ("tr" ).First ()
385+ val , _ := previousTr .Attr ("data-line-type" )
386+ assert .Equal (t , "add" , val )
387+ td := previousTr .Find ("td.lines-num-new" )
388+ val , _ = td .Attr ("data-line-num" )
389+ assert .Equal (t , "4" , val )
390+ })
391+
392+ t .Run ("Pushing new commit to the pull request to add lines" , func (t * testing.T ) {
393+ // create a new branch to prepare for pull request
394+ _ , err = files_service .ChangeRepoFiles (db .DefaultContext , repo , user2 , & files_service.ChangeRepoFilesOptions {
395+ OldBranch : "branch_codecomment1" ,
396+ Files : []* files_service.ChangeRepoFile {
397+ {
398+ Operation : "update" ,
399+ TreePath : "README.md" ,
400+ // add 1 line before the code comment line 4
401+ ContentReader : strings .NewReader ("# 111\n # 222\n # 333\n # 334\n # 444\n # 555\n # 666\n # 777\n " ),
402+ },
403+ },
404+ })
405+ assert .NoError (t , err )
406+
407+ session := loginUser (t , "user2" )
408+ comment := unittest .AssertExistsAndLoadBean (t , & issues_model.Comment {
409+ Type : issues_model .CommentTypeCode ,
410+ IssueID : pr .IssueID ,
411+ })
412+
413+ // load the files page and confirm the comment's line number is dynamically adjusted
414+ filesPageURL := fmt .Sprintf ("/user2/test_codecomment/pulls/%d/files" , pr .Index )
415+ req := NewRequest (t , "GET" , filesPageURL )
416+ resp := session .MakeRequest (t , req , http .StatusOK )
417+ htmlDoc := NewHTMLParser (t , resp .Body )
418+ commentHTML := htmlDoc .Find (fmt .Sprintf ("#issuecomment-%d" , comment .ID ))
419+ assert .NotNil (t , commentHTML )
420+ assert .Equal (t , "code comment on right line 4" , strings .TrimSpace (commentHTML .Find (".comment-body .render-content" ).Text ()))
421+
422+ // the last line of this comment line number is 4
423+ parentTr := commentHTML .ParentsFiltered ("tr" ).First ()
424+ assert .NotNil (t , parentTr )
425+ previousTr := parentTr .PrevAllFiltered ("tr" ).First ()
426+ val , _ := previousTr .Attr ("data-line-type" )
427+ assert .Equal (t , "add" , val )
428+ td := previousTr .Find ("td.lines-num-new" )
429+ val , _ = td .Attr ("data-line-num" )
430+ assert .Equal (t , "5" , val ) // one line have inserted in this commit, so the line number should be 5 now
431+ })
432+
433+ t .Run ("Pushing new commit to the pull request to delete lines" , func (t * testing.T ) {
434+ // create a new branch to prepare for pull request
435+ _ , err := files_service .ChangeRepoFiles (db .DefaultContext , repo , user2 , & files_service.ChangeRepoFilesOptions {
436+ OldBranch : "branch_codecomment1" ,
437+ Files : []* files_service.ChangeRepoFile {
438+ {
439+ Operation : "update" ,
440+ TreePath : "README.md" ,
441+ // delete the second line before the code comment line 4
442+ ContentReader : strings .NewReader ("# 111\n # 333\n # 334\n # 444\n # 555\n # 666\n # 777\n " ),
443+ },
444+ },
445+ })
446+ assert .NoError (t , err )
447+
448+ diffContent , _ , err := git .NewCommand ("diff" ).AddDynamicArguments (pr .MergeBase , pr .GetGitHeadRefName ()).RunStdString (
449+ t .Context (), & git.RunOpts {
450+ Dir : repo .RepoPath (),
451+ },
452+ )
453+ assert .NoError (t , err )
454+ fmt .Println ("=======" )
455+ fmt .Println (diffContent )
456+ fmt .Println ("=======" )
457+
458+ session := loginUser (t , "user2" )
459+ comment := unittest .AssertExistsAndLoadBean (t , & issues_model.Comment {
460+ Type : issues_model .CommentTypeCode ,
461+ IssueID : pr .IssueID ,
462+ })
463+
464+ // load the files page and confirm the comment's line number is dynamically adjusted
465+ filesPageURL := fmt .Sprintf ("/user2/test_codecomment/pulls/%d/files" , pr .Index )
466+ req := NewRequest (t , "GET" , filesPageURL )
467+ resp := session .MakeRequest (t , req , http .StatusOK )
468+ htmlDoc := NewHTMLParser (t , resp .Body )
469+ commentHTML := htmlDoc .Find (fmt .Sprintf ("#issuecomment-%d" , comment .ID ))
470+ assert .NotNil (t , commentHTML )
471+ assert .Equal (t , "code comment on right line 4" , strings .TrimSpace (commentHTML .Find (".comment-body .render-content" ).Text ()))
472+
473+ // the last line of this comment line number is 4
474+ parentTr := commentHTML .ParentsFiltered ("tr" ).First ()
475+ assert .NotNil (t , parentTr )
476+ previousTr := parentTr .PrevAllFiltered ("tr" ).First ()
477+ val , _ := previousTr .Attr ("data-line-type" )
478+ assert .Equal (t , "add" , val )
479+ td := previousTr .Find ("td.lines-num-new" )
480+ val , _ = td .Attr ("data-line-num" )
481+ assert .Equal (t , "4" , val ) // one line have inserted and one line deleted before this line in this commit, so the line number should be 4 now
482+
483+ // add a new comment on the deleted line
484+ commentURL := fmt .Sprintf ("/user2/test_codecomment/pulls/%d/files/reviews/comments" , pr .Index )
485+ options := map [string ]string {
486+ "_csrf" : htmlDoc .GetCSRF (),
487+ "origin" : "diff" ,
488+ "content" : "code comment on left line 2" ,
489+ "side" : "previous" ,
490+ "line" : "2" ,
491+ "path" : "README.md" ,
492+ "single_review" : "true" ,
493+ "reply" : "0" ,
494+ "before_commit_id" : "" ,
495+ "after_commit_id" : "" ,
496+ }
497+ req = NewRequestWithValues (t , "POST" , commentURL , options )
498+ session .MakeRequest (t , req , http .StatusOK )
499+ // Check if the comment was created.
500+ commentLast := unittest .AssertExistsAndLoadBean (t , & issues_model.Comment {
501+ Type : issues_model .CommentTypeCode ,
502+ IssueID : pr .IssueID ,
503+ Content : "code comment on left line 2" ,
504+ })
505+ assert .Equal (t , "code comment on left line 2" , commentLast .Content )
506+ assert .Equal (t , "README.md" , commentLast .TreePath )
507+ assert .Equal (t , int64 (- 2 ), commentLast .Line )
508+ assert .Equal (t , pr .MergeBase , commentLast .CommitSHA )
509+
510+ // load the files page and confirm the comment's line number is dynamically adjusted
511+ filesPageURL = fmt .Sprintf ("/user2/test_codecomment/pulls/%d/files" , pr .Index )
512+ req = NewRequest (t , "GET" , filesPageURL )
513+ resp = session .MakeRequest (t , req , http .StatusOK )
514+ htmlDoc = NewHTMLParser (t , resp .Body )
515+ commentHTML = htmlDoc .Find (fmt .Sprintf ("#issuecomment-%d" , commentLast .ID ))
516+ assert .NotNil (t , commentHTML )
517+ assert .Equal (t , "code comment on left line 2" , strings .TrimSpace (commentHTML .Find (".comment-body .render-content" ).Text ()))
518+
519+ // the last line of this comment line number is 4
520+ parentTr = commentHTML .ParentsFiltered ("tr" ).First ()
521+ assert .NotNil (t , parentTr )
522+ previousTr = parentTr .PrevAllFiltered ("tr" ).First ()
523+ val , _ = previousTr .Attr ("data-line-type" )
524+ assert .Equal (t , "del" , val )
525+ td = previousTr .Find ("td.lines-num-old" )
526+ val , _ = td .Attr ("data-line-num" )
527+ assert .Equal (t , "2" , val )
528+ })
529+ })
530+ }
0 commit comments