blob: 031a738bebf642a65cea0739ee0d8a6e1438109e [file] [log] [blame]
/**
* @license
* Copyright 2020 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
import '../test/common-test-setup';
import {
isUnresolved,
getPatchRangeForCommentUrl,
createCommentThreads,
sortComments,
USER_SUGGESTION_START_PATTERN,
hasUserSuggestion,
getUserSuggestion,
getContentInCommentRange,
createUserFixSuggestion,
PROVIDED_FIX_ID,
getMentionedThreads,
isNewThread,
createNew,
getUserSuggestionFromString,
} from './comment-util';
import {
createAccountWithEmail,
createComment,
createCommentThread,
} from '../test/test-data-generators';
import {CommentSide, SpecialFilePath} from '../constants/constants';
import {
Comment,
SavingState,
PARENT,
RevisionPatchSetNum,
Timestamp,
UrlEncodedCommentId,
} from '../types/common';
import {assert} from '@open-wc/testing';
suite('comment-util', () => {
test('isUnresolved', () => {
const thread = createCommentThread([createComment()]);
assert.isFalse(isUnresolved(thread));
assert.isTrue(
isUnresolved({
...thread,
comments: [{...createComment(), unresolved: true}],
})
);
assert.isFalse(
isUnresolved({
...thread,
comments: [{...createComment(), unresolved: false}],
})
);
assert.isTrue(
isUnresolved({
...thread,
comments: [
{...createComment(), unresolved: false},
{...createComment(), unresolved: true},
],
})
);
assert.isFalse(
isUnresolved({
...thread,
comments: [
{...createComment(), unresolved: true},
{...createComment(), unresolved: false},
],
})
);
});
test('isNewThread', () => {
let thread = createCommentThread([createComment()]);
assert.isFalse(isNewThread(thread));
thread = createCommentThread([createComment(), createNew()]);
assert.isFalse(isNewThread(thread));
thread = createCommentThread([createNew()]);
assert.isTrue(isNewThread(thread));
});
suite('getPatchRangeForCommentUrl', () => {
test('comment created with side=PARENT does not navigate to latest ps', () => {
const comment = {
...createComment(),
id: 'c4' as UrlEncodedCommentId,
line: 10,
patch_set: 4 as RevisionPatchSetNum,
side: CommentSide.PARENT,
path: '/COMMIT_MSG',
};
assert.deepEqual(
getPatchRangeForCommentUrl(comment, 11 as RevisionPatchSetNum),
{
basePatchNum: PARENT,
patchNum: 4 as RevisionPatchSetNum,
}
);
});
});
test('getMentionedThreads', () => {
const account = createAccountWithEmail('abcd@def.com');
const threads = [
createCommentThread([
{
...createComment(),
message: 'random text with no emails',
},
]),
createCommentThread([
{
...createComment(),
message: '@abcd@def.com please take a look',
},
{
...createComment(),
message: '@abcd@def.com please take a look again at this',
},
]),
createCommentThread([
{
...createComment(),
message: '@abcd@def.com this is important',
},
]),
];
assert.deepEqual(getMentionedThreads(threads, account), [
threads[1],
threads[2],
]);
assert.deepEqual(
getMentionedThreads(threads, createAccountWithEmail('xyz@def.com')),
[]
);
});
test('comments sorting', () => {
const updated = '2023-12-24 12:00:00.123000000' as Timestamp;
const comments: Comment[] = [
{
id: 'pslevel' as UrlEncodedCommentId,
path: SpecialFilePath.PATCHSET_LEVEL_COMMENTS,
updated,
},
{
id: 'commit-message' as UrlEncodedCommentId,
path: SpecialFilePath.COMMIT_MESSAGE,
updated,
},
{
id: 'path2-id1-updated-earlier' as UrlEncodedCommentId,
path: 'path2',
updated: '2023-12-23 12:00:00.123000000' as Timestamp,
},
{
id: 'path2-id1' as UrlEncodedCommentId,
path: 'path2',
updated,
},
{
id: 'path2-id2' as UrlEncodedCommentId,
path: 'path2',
updated,
},
{
id: 'path2-id1-updated-later' as UrlEncodedCommentId,
path: 'path2',
updated: '2023-12-24 15:55:00.123000000' as Timestamp,
},
{
id: 'path2-line1' as UrlEncodedCommentId,
path: 'path2',
line: 1,
updated,
},
{
id: 'path2-line2' as UrlEncodedCommentId,
path: 'path2',
line: 2,
updated,
},
{
id: 'range-1-0-2-0' as UrlEncodedCommentId,
path: 'path2',
line: 2,
range: {
start_line: 1,
start_character: 0,
end_line: 2,
end_character: 0,
},
updated,
},
{
id: 'range-1-0-3-0' as UrlEncodedCommentId,
path: 'path2',
line: 2,
range: {
start_line: 1,
start_character: 0,
end_line: 3,
end_character: 0,
},
updated,
},
{
id: 'range-2-0-3-0' as UrlEncodedCommentId,
path: 'path2',
line: 2,
range: {
start_line: 2,
start_character: 0,
end_line: 3,
end_character: 0,
},
updated,
},
{
id: 'range-2-0-3-5' as UrlEncodedCommentId,
path: 'path2',
line: 2,
range: {
start_line: 2,
start_character: 0,
end_line: 3,
end_character: 5,
},
updated,
},
{
id: 'range-2-5-3-5' as UrlEncodedCommentId,
path: 'path2',
line: 2,
range: {
start_line: 2,
start_character: 5,
end_line: 3,
end_character: 5,
},
updated,
},
{
id: 'path2-line2-ps1' as UrlEncodedCommentId,
path: 'path2',
line: 2,
patch_set: 1 as RevisionPatchSetNum,
updated,
},
{
id: 'path2-line2-ps2' as UrlEncodedCommentId,
path: 'path2',
line: 2,
patch_set: 2 as RevisionPatchSetNum,
updated,
},
{
id: 'path2-line2-ps2-updated-later' as UrlEncodedCommentId,
path: 'path2',
line: 2,
patch_set: 2 as RevisionPatchSetNum,
updated,
},
{
client_id: 'new1' as UrlEncodedCommentId,
client_created_ms: 1,
savingState: SavingState.OK,
path: 'path2',
line: 2,
patch_set: 2 as RevisionPatchSetNum,
},
{
client_id: 'new2' as UrlEncodedCommentId,
client_created_ms: 2,
savingState: SavingState.OK,
path: 'path2',
line: 2,
patch_set: 2 as RevisionPatchSetNum,
},
{
client_id: 'new2-sort-by-id' as UrlEncodedCommentId,
client_created_ms: 2,
savingState: SavingState.OK,
path: 'path2',
line: 2,
patch_set: 2 as RevisionPatchSetNum,
},
];
const shuffled = [...comments].sort(() => Math.random() - 0.5);
const sorted = sortComments(shuffled);
assert.sameOrderedMembers(comments, sorted);
});
suite('createCommentThreads', () => {
test('creates threads from individual comments', () => {
const comments: Comment[] = [
{
id: 'sallys_confession' as UrlEncodedCommentId,
message: 'i like you, jack',
updated: '2015-12-23 15:00:20.396000000' as Timestamp,
line: 1,
patch_set: 1 as RevisionPatchSetNum,
path: 'some/path',
},
{
id: 'jacks_reply' as UrlEncodedCommentId,
message: 'i like you, too',
updated: '2015-12-24 15:01:20.396000000' as Timestamp,
line: 1,
in_reply_to: 'sallys_confession' as UrlEncodedCommentId,
patch_set: 1 as RevisionPatchSetNum,
path: 'some/path',
},
{
id: 'new_draft' as UrlEncodedCommentId,
message: 'i do not like either of you' as UrlEncodedCommentId,
savingState: SavingState.OK,
updated: '2015-12-20 15:01:20.396000000' as Timestamp,
line: 1,
patch_set: 1 as RevisionPatchSetNum,
path: 'some/path',
},
];
const actualThreads = createCommentThreads(comments);
assert.equal(actualThreads.length, 2);
assert.equal(actualThreads[0].comments.length, 2);
assert.deepEqual(actualThreads[0].comments[0], comments[0]);
assert.deepEqual(actualThreads[0].comments[1], comments[1]);
assert.equal(actualThreads[0].patchNum, 1 as RevisionPatchSetNum);
assert.equal(actualThreads[0].line, 1);
assert.equal(actualThreads[1].comments.length, 1);
assert.deepEqual(actualThreads[1].comments[0], comments[2]);
assert.equal(actualThreads[1].patchNum, 1 as RevisionPatchSetNum);
assert.equal(actualThreads[1].line, 1);
});
test('derives patchNum and range', () => {
const comments = [
{
id: 'betsys_confession' as UrlEncodedCommentId,
message: 'i like you, jack',
updated: '2015-12-24 15:00:10.396000000' as Timestamp,
range: {
start_line: 1,
start_character: 1,
end_line: 1,
end_character: 2,
},
patch_set: 5 as RevisionPatchSetNum,
path: '/p',
line: 1,
},
];
const expectedThreads = [
{
commentSide: CommentSide.REVISION,
path: '/p',
rootId: 'betsys_confession' as UrlEncodedCommentId,
mergeParentNum: undefined,
comments: [
{
id: 'betsys_confession' as UrlEncodedCommentId,
path: '/p',
message: 'i like you, jack',
updated: '2015-12-24 15:00:10.396000000' as Timestamp,
range: {
start_line: 1,
start_character: 1,
end_line: 1,
end_character: 2,
},
patch_set: 5 as RevisionPatchSetNum,
line: 1,
},
],
patchNum: 5 as RevisionPatchSetNum,
range: {
start_line: 1,
start_character: 1,
end_line: 1,
end_character: 2,
},
line: 1,
},
];
assert.deepEqual(createCommentThreads(comments), expectedThreads);
});
test('does not thread unrelated comments at same location', () => {
const comments = [
{
id: 'sallys_confession' as UrlEncodedCommentId,
message: 'i like you, jack',
updated: '2015-12-23 15:00:20.396000000' as Timestamp,
path: '/p',
},
{
id: 'jacks_reply' as UrlEncodedCommentId,
message: 'i like you, too',
updated: '2015-12-24 15:01:20.396000000' as Timestamp,
path: '/p',
},
];
assert.equal(createCommentThreads(comments).length, 2);
});
});
test('hasUserSuggestion', () => {
const comment = {
...createComment(),
message: `${USER_SUGGESTION_START_PATTERN}${'test'}${'\n```'}`,
};
assert.isTrue(hasUserSuggestion(comment));
});
test('getUserSuggestion', () => {
const suggestion = 'test';
const comment = {
...createComment(),
message: `${USER_SUGGESTION_START_PATTERN}${suggestion}${'\n```'}`,
};
assert.equal(getUserSuggestion(comment), suggestion);
});
suite('getContentInCommentRange', () => {
test('one line', () => {
const comment = {
...createComment(),
line: 1,
};
const content = 'line1\nline2\nline3';
assert.equal(getContentInCommentRange(content, comment), 'line1');
});
test('multi line', () => {
const comment = {
...createComment(),
line: 3,
range: {
start_line: 1,
start_character: 5,
end_line: 3,
end_character: 39,
},
};
const selectedText =
' * Examples:\n' +
' * Acknowledge/Dismiss, Delete, Report a bug, Report as not useful,\n' +
' * Make blocking, Downgrade severity.';
const content = `${selectedText}\n`;
assert.equal(getContentInCommentRange(content, comment), selectedText);
});
});
suite('createUserFixSuggestion', () => {
test('one line', () => {
const comment = {
...createComment(),
line: 1,
path: 'abc.txt',
};
const line = 'lane1';
const replacement = 'line1';
assert.deepEqual(createUserFixSuggestion(comment, line, replacement), [
{
fix_id: PROVIDED_FIX_ID,
description: 'User suggestion',
replacements: [
{
path: 'abc.txt',
range: {
start_line: 1,
start_character: 0,
end_line: 1,
end_character: line.length,
},
replacement,
},
],
},
]);
});
test('multiline', () => {
const comment = {
...createComment(),
line: 3,
range: {
start_line: 1,
start_character: 5,
end_line: 3,
end_character: 39,
},
path: 'abc.txt',
};
const line =
' * Examples:\n' +
' * Acknowledge/Dismiss, Delete, Report a bug, Report as not useful,\n' +
' * Make blocking, Downgrade severity.';
const replacement =
' - Examples:\n' +
' - Acknowledge/Dismiss, Delete, Report a bug, Report as not useful,\n' +
' - Make blocking, Downgrade severity.';
assert.deepEqual(createUserFixSuggestion(comment, line, replacement), [
{
fix_id: PROVIDED_FIX_ID,
description: 'User suggestion',
replacements: [
{
path: 'abc.txt',
range: {
start_line: 1,
start_character: 0,
end_line: 3,
end_character: 42,
},
replacement,
},
],
},
]);
});
});
suite('getUserSuggestionFromString', () => {
const createSuggestionContent = (suggestions: string[]) =>
suggestions
.map(s => `${USER_SUGGESTION_START_PATTERN}${s}\n\`\`\``)
.join('\n');
test('returns empty string for content without suggestions', () => {
const content = 'This is a comment without any suggestions.';
assert.equal(getUserSuggestionFromString(content), '');
});
test('returns first suggestion when no index is provided', () => {
const content = createSuggestionContent(['First suggestion']);
assert.equal(getUserSuggestionFromString(content), 'First suggestion');
});
test('returns correct suggestion for given index', () => {
const content = createSuggestionContent([
'First suggestion',
'Second suggestion',
'Third suggestion',
]);
assert.equal(
getUserSuggestionFromString(content, 1),
'Second suggestion'
);
});
test('returns last suggestion when index is out of bounds', () => {
const content = createSuggestionContent([
'First suggestion',
'Second suggestion',
]);
assert.equal(
getUserSuggestionFromString(content, 5),
'Second suggestion'
);
});
test('handles suggestion without closing backticks', () => {
const content = `${USER_SUGGESTION_START_PATTERN}Unclosed suggestion`;
assert.equal(getUserSuggestionFromString(content), 'Unclosed suggestion');
});
test('handles multiple suggestions with varying content', () => {
const content = createSuggestionContent([
'First\nMultiline\nSuggestion',
'Second suggestion',
'Third suggestion with `backticks`',
]);
assert.equal(
getUserSuggestionFromString(content, 0),
'First\nMultiline\nSuggestion'
);
assert.equal(
getUserSuggestionFromString(content, 1),
'Second suggestion'
);
assert.equal(
getUserSuggestionFromString(content, 2),
'Third suggestion with `backticks`'
);
});
});
});