Ported comments now contribute to the thread, unresolved and draft count shown on the file list and the diff view dropdown. For the file list count and the file dropdown in diff view, we have a path and patchset range already defined. We then figure out if the ported thread will be rendered or not and add it to the count accordingly. Also refactor to * Use getThreadsBySideForFile to calculate thread count which automatically includes ported comments since we are now calculating these numbers based on threads and not comments. * Merge comment string computed by gr-file-list and gr-diff-view and move it to gr-comment-api. Change-Id: Ibf6959abf5a0cf437a4964ef32e35f6304d0de86
		
			
				
	
	
		
			572 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
			
		
		
	
	
			572 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
/**
 | 
						|
 * @license
 | 
						|
 * Copyright (C) 2020 The Android Open Source Project
 | 
						|
 *
 | 
						|
 * 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.
 | 
						|
 */
 | 
						|
 | 
						|
import {
 | 
						|
  AccountId,
 | 
						|
  AccountInfo,
 | 
						|
  AccountsConfigInfo,
 | 
						|
  ApprovalInfo,
 | 
						|
  AuthInfo,
 | 
						|
  BranchName,
 | 
						|
  ChangeConfigInfo,
 | 
						|
  ChangeId,
 | 
						|
  ChangeInfo,
 | 
						|
  ChangeInfoId,
 | 
						|
  ChangeMessageId,
 | 
						|
  ChangeMessageInfo,
 | 
						|
  ChangeViewChangeInfo,
 | 
						|
  CommentLinkInfo,
 | 
						|
  CommentLinks,
 | 
						|
  CommitId,
 | 
						|
  CommitInfo,
 | 
						|
  ConfigInfo,
 | 
						|
  DownloadInfo,
 | 
						|
  EditPatchSetNum,
 | 
						|
  GerritInfo,
 | 
						|
  EmailAddress,
 | 
						|
  GitPersonInfo,
 | 
						|
  GitRef,
 | 
						|
  InheritedBooleanInfo,
 | 
						|
  MaxObjectSizeLimitInfo,
 | 
						|
  MergeableInfo,
 | 
						|
  NumericChangeId,
 | 
						|
  PatchSetNum,
 | 
						|
  PluginConfigInfo,
 | 
						|
  PreferencesInfo,
 | 
						|
  RepoName,
 | 
						|
  Reviewers,
 | 
						|
  RevisionInfo,
 | 
						|
  SchemesInfoMap,
 | 
						|
  ServerInfo,
 | 
						|
  SubmitTypeInfo,
 | 
						|
  SuggestInfo,
 | 
						|
  Timestamp,
 | 
						|
  TimezoneOffset,
 | 
						|
  UserConfigInfo,
 | 
						|
  AccountDetailInfo,
 | 
						|
  Requirement,
 | 
						|
  RequirementType,
 | 
						|
  UrlEncodedCommentId,
 | 
						|
} from '../types/common';
 | 
						|
import {
 | 
						|
  AccountsVisibility,
 | 
						|
  AppTheme,
 | 
						|
  AuthType,
 | 
						|
  ChangeStatus,
 | 
						|
  DateFormat,
 | 
						|
  DefaultBase,
 | 
						|
  DefaultDisplayNameConfig,
 | 
						|
  DiffViewMode,
 | 
						|
  EmailStrategy,
 | 
						|
  InheritedBooleanInfoConfiguredValue,
 | 
						|
  MergeabilityComputationBehavior,
 | 
						|
  RevisionKind,
 | 
						|
  SubmitType,
 | 
						|
  TimeFormat,
 | 
						|
  RequirementStatus,
 | 
						|
  CommentSide,
 | 
						|
} from '../constants/constants';
 | 
						|
import {formatDate} from '../utils/date-util';
 | 
						|
import {GetDiffCommentsOutput} from '../services/services/gr-rest-api/gr-rest-api';
 | 
						|
import {AppElementChangeViewParams} from '../elements/gr-app-types';
 | 
						|
import {
 | 
						|
  EditRevisionInfo,
 | 
						|
  ParsedChangeInfo,
 | 
						|
} from '../elements/shared/gr-rest-api-interface/gr-reviewer-updates-parser';
 | 
						|
import {CommitInfoWithRequiredCommit} from '../elements/change/gr-change-metadata/gr-change-metadata';
 | 
						|
import {WebLinkInfo} from '../types/diff';
 | 
						|
import {UIComment, UIDraft} from '../utils/comment-util';
 | 
						|
import {GerritView} from '../services/router/router-model';
 | 
						|
import {ChangeComments} from '../elements/diff/gr-comment-api/gr-comment-api';
 | 
						|
 | 
						|
export function dateToTimestamp(date: Date): Timestamp {
 | 
						|
  const nanosecondSuffix = '.000000000';
 | 
						|
  return (formatDate(date, 'YYYY-MM-DD HH:mm:ss') +
 | 
						|
    nanosecondSuffix) as Timestamp;
 | 
						|
}
 | 
						|
 | 
						|
export function createCommentLink(match = 'test'): CommentLinkInfo {
 | 
						|
  return {
 | 
						|
    match,
 | 
						|
  };
 | 
						|
}
 | 
						|
 | 
						|
export function createInheritedBoolean(value = false): InheritedBooleanInfo {
 | 
						|
  return {
 | 
						|
    value,
 | 
						|
    configured_value: value
 | 
						|
      ? InheritedBooleanInfoConfiguredValue.TRUE
 | 
						|
      : InheritedBooleanInfoConfiguredValue.FALSE,
 | 
						|
  };
 | 
						|
}
 | 
						|
 | 
						|
export function createMaxObjectSizeLimit(): MaxObjectSizeLimitInfo {
 | 
						|
  return {};
 | 
						|
}
 | 
						|
 | 
						|
export function createSubmitType(
 | 
						|
  value: Exclude<SubmitType, SubmitType.INHERIT> = SubmitType.MERGE_IF_NECESSARY
 | 
						|
): SubmitTypeInfo {
 | 
						|
  return {
 | 
						|
    value,
 | 
						|
    configured_value: SubmitType.INHERIT,
 | 
						|
    inherited_value: value,
 | 
						|
  };
 | 
						|
}
 | 
						|
 | 
						|
export function createCommentLinks(): CommentLinks {
 | 
						|
  return {};
 | 
						|
}
 | 
						|
 | 
						|
export function createConfig(): ConfigInfo {
 | 
						|
  return {
 | 
						|
    private_by_default: createInheritedBoolean(),
 | 
						|
    work_in_progress_by_default: createInheritedBoolean(),
 | 
						|
    max_object_size_limit: createMaxObjectSizeLimit(),
 | 
						|
    default_submit_type: createSubmitType(),
 | 
						|
    submit_type: SubmitType.INHERIT,
 | 
						|
    commentlinks: createCommentLinks(),
 | 
						|
  };
 | 
						|
}
 | 
						|
 | 
						|
export function createAccountWithId(id = 5): AccountInfo {
 | 
						|
  return {
 | 
						|
    _account_id: id as AccountId,
 | 
						|
  };
 | 
						|
}
 | 
						|
 | 
						|
export function createAccountDetailWithId(id = 5): AccountDetailInfo {
 | 
						|
  return {
 | 
						|
    _account_id: id as AccountId,
 | 
						|
    registered_on: dateToTimestamp(new Date(2020, 10, 15, 14, 5, 8)),
 | 
						|
  };
 | 
						|
}
 | 
						|
 | 
						|
export function createAccountWithEmail(email = 'test@'): AccountInfo {
 | 
						|
  return {
 | 
						|
    email: email as EmailAddress,
 | 
						|
  };
 | 
						|
}
 | 
						|
 | 
						|
export function createAccountWithIdNameAndEmail(id = 5): AccountInfo {
 | 
						|
  return {
 | 
						|
    _account_id: id as AccountId,
 | 
						|
    email: `user-${id}@` as EmailAddress,
 | 
						|
    name: `User-${id}`,
 | 
						|
  };
 | 
						|
}
 | 
						|
 | 
						|
export function createReviewers(): Reviewers {
 | 
						|
  return {};
 | 
						|
}
 | 
						|
 | 
						|
export const TEST_PROJECT_NAME: RepoName = 'test-project' as RepoName;
 | 
						|
export const TEST_BRANCH_ID: BranchName = 'test-branch' as BranchName;
 | 
						|
export const TEST_CHANGE_ID: ChangeId = 'TestChangeId' as ChangeId;
 | 
						|
export const TEST_CHANGE_INFO_ID: ChangeInfoId = `${TEST_PROJECT_NAME}~${TEST_BRANCH_ID}~${TEST_CHANGE_ID}` as ChangeInfoId;
 | 
						|
export const TEST_SUBJECT = 'Test subject';
 | 
						|
export const TEST_NUMERIC_CHANGE_ID = 42 as NumericChangeId;
 | 
						|
 | 
						|
export const TEST_CHANGE_CREATED = new Date(2020, 1, 1, 1, 2, 3);
 | 
						|
export const TEST_CHANGE_UPDATED = new Date(2020, 10, 6, 5, 12, 34);
 | 
						|
 | 
						|
export function createGitPerson(name = 'Test name'): GitPersonInfo {
 | 
						|
  return {
 | 
						|
    name,
 | 
						|
    email: `${name}@`,
 | 
						|
    date: dateToTimestamp(new Date(2019, 11, 6, 14, 5, 8)),
 | 
						|
    tz: 0 as TimezoneOffset,
 | 
						|
  };
 | 
						|
}
 | 
						|
 | 
						|
export function createCommit(): CommitInfo {
 | 
						|
  return {
 | 
						|
    parents: [],
 | 
						|
    author: createGitPerson(),
 | 
						|
    committer: createGitPerson(),
 | 
						|
    subject: 'Test commit subject',
 | 
						|
    message: 'Test commit message',
 | 
						|
  };
 | 
						|
}
 | 
						|
 | 
						|
export function createCommitInfoWithRequiredCommit(): CommitInfoWithRequiredCommit {
 | 
						|
  return {
 | 
						|
    ...createCommit(),
 | 
						|
    commit: 'commit' as CommitId,
 | 
						|
  };
 | 
						|
}
 | 
						|
 | 
						|
export function createRevision(patchSetNum = 1): RevisionInfo {
 | 
						|
  return {
 | 
						|
    _number: patchSetNum as PatchSetNum,
 | 
						|
    commit: createCommit(),
 | 
						|
    created: dateToTimestamp(TEST_CHANGE_CREATED),
 | 
						|
    kind: RevisionKind.REWORK,
 | 
						|
    ref: 'refs/changes/5/6/1' as GitRef,
 | 
						|
    uploader: createAccountWithId(),
 | 
						|
  };
 | 
						|
}
 | 
						|
 | 
						|
export function createEditRevision(): EditRevisionInfo {
 | 
						|
  return {
 | 
						|
    _number: EditPatchSetNum,
 | 
						|
    basePatchNum: 1 as PatchSetNum,
 | 
						|
    commit: createCommit(),
 | 
						|
  };
 | 
						|
}
 | 
						|
 | 
						|
export function createChangeMessage(id = 'cm_id_1'): ChangeMessageInfo {
 | 
						|
  return {
 | 
						|
    id: id as ChangeMessageId,
 | 
						|
    date: dateToTimestamp(TEST_CHANGE_CREATED),
 | 
						|
    message: `This is a message with id ${id}`,
 | 
						|
  };
 | 
						|
}
 | 
						|
 | 
						|
export function createRevisions(
 | 
						|
  count: number
 | 
						|
): {[revisionId: string]: RevisionInfo} {
 | 
						|
  const revisions: {[revisionId: string]: RevisionInfo} = {};
 | 
						|
  const revisionDate = TEST_CHANGE_CREATED;
 | 
						|
  const revisionIdStart = 1; // The same as getCurrentRevision
 | 
						|
  for (let i = 0; i < count; i++) {
 | 
						|
    const revisionId = (i + revisionIdStart).toString(16);
 | 
						|
    const revision: RevisionInfo = {
 | 
						|
      ...createRevision(i + 1),
 | 
						|
      created: dateToTimestamp(revisionDate),
 | 
						|
      ref: `refs/changes/5/6/${i + 1}` as GitRef,
 | 
						|
    };
 | 
						|
    revisions[revisionId] = revision;
 | 
						|
    // advance 1 day
 | 
						|
    revisionDate.setDate(revisionDate.getDate() + 1);
 | 
						|
  }
 | 
						|
  return revisions;
 | 
						|
}
 | 
						|
 | 
						|
export function getCurrentRevision(count: number): CommitId {
 | 
						|
  const revisionIdStart = 1; // The same as createRevisions
 | 
						|
  return (count + revisionIdStart).toString(16) as CommitId;
 | 
						|
}
 | 
						|
 | 
						|
export function createChangeMessages(count: number): ChangeMessageInfo[] {
 | 
						|
  const messageIdStart = 1000;
 | 
						|
  const messages: ChangeMessageInfo[] = [];
 | 
						|
  const messageDate = TEST_CHANGE_CREATED;
 | 
						|
  for (let i = 0; i < count; i++) {
 | 
						|
    messages.push({
 | 
						|
      ...createChangeMessage((i + messageIdStart).toString(16)),
 | 
						|
      date: dateToTimestamp(messageDate),
 | 
						|
    });
 | 
						|
    messageDate.setDate(messageDate.getDate() + 1);
 | 
						|
  }
 | 
						|
  return messages;
 | 
						|
}
 | 
						|
 | 
						|
export function createChange(): ChangeInfo {
 | 
						|
  return {
 | 
						|
    id: TEST_CHANGE_INFO_ID,
 | 
						|
    project: TEST_PROJECT_NAME,
 | 
						|
    branch: TEST_BRANCH_ID,
 | 
						|
    change_id: TEST_CHANGE_ID,
 | 
						|
    subject: TEST_SUBJECT,
 | 
						|
    status: ChangeStatus.NEW,
 | 
						|
    created: dateToTimestamp(TEST_CHANGE_CREATED),
 | 
						|
    updated: dateToTimestamp(TEST_CHANGE_UPDATED),
 | 
						|
    insertions: 0,
 | 
						|
    deletions: 0,
 | 
						|
    _number: TEST_NUMERIC_CHANGE_ID,
 | 
						|
    owner: createAccountWithId(),
 | 
						|
    // This is documented as optional, but actually always set.
 | 
						|
    reviewers: createReviewers(),
 | 
						|
  };
 | 
						|
}
 | 
						|
 | 
						|
export function createChangeViewChange(): ChangeViewChangeInfo {
 | 
						|
  return {
 | 
						|
    ...createChange(),
 | 
						|
    revisions: {
 | 
						|
      abc: createRevision(),
 | 
						|
    },
 | 
						|
    current_revision: 'abc' as CommitId,
 | 
						|
  };
 | 
						|
}
 | 
						|
 | 
						|
export function createParsedChange(): ParsedChangeInfo {
 | 
						|
  return createChangeViewChange();
 | 
						|
}
 | 
						|
 | 
						|
export function createAccountsConfig(): AccountsConfigInfo {
 | 
						|
  return {
 | 
						|
    visibility: AccountsVisibility.ALL,
 | 
						|
    default_display_name: DefaultDisplayNameConfig.FULL_NAME,
 | 
						|
  };
 | 
						|
}
 | 
						|
 | 
						|
export function createAuth(): AuthInfo {
 | 
						|
  return {
 | 
						|
    auth_type: AuthType.OPENID,
 | 
						|
    editable_account_fields: [],
 | 
						|
  };
 | 
						|
}
 | 
						|
 | 
						|
export function createChangeConfig(): ChangeConfigInfo {
 | 
						|
  return {
 | 
						|
    large_change: 500,
 | 
						|
    reply_label: 'Reply',
 | 
						|
    reply_tooltip: 'Reply and score',
 | 
						|
    // The default update_delay is 5 minutes, but we don't want to accidentally
 | 
						|
    // start polling in tests
 | 
						|
    update_delay: 0,
 | 
						|
    mergeability_computation_behavior:
 | 
						|
      MergeabilityComputationBehavior.REF_UPDATED_AND_CHANGE_REINDEX,
 | 
						|
    enable_attention_set: false,
 | 
						|
    enable_assignee: false,
 | 
						|
  };
 | 
						|
}
 | 
						|
 | 
						|
export function createDownloadSchemes(): SchemesInfoMap {
 | 
						|
  return {};
 | 
						|
}
 | 
						|
 | 
						|
export function createDownloadInfo(): DownloadInfo {
 | 
						|
  return {
 | 
						|
    schemes: createDownloadSchemes(),
 | 
						|
    archives: ['tgz', 'tar'],
 | 
						|
  };
 | 
						|
}
 | 
						|
 | 
						|
export function createGerritInfo(): GerritInfo {
 | 
						|
  return {
 | 
						|
    all_projects: 'All-Projects',
 | 
						|
    all_users: 'All-Users',
 | 
						|
    doc_search: false,
 | 
						|
  };
 | 
						|
}
 | 
						|
 | 
						|
export function createPluginConfig(): PluginConfigInfo {
 | 
						|
  return {
 | 
						|
    has_avatars: false,
 | 
						|
    js_resource_paths: [],
 | 
						|
    html_resource_paths: [],
 | 
						|
  };
 | 
						|
}
 | 
						|
 | 
						|
export function createSuggestInfo(): SuggestInfo {
 | 
						|
  return {
 | 
						|
    from: 0,
 | 
						|
  };
 | 
						|
}
 | 
						|
 | 
						|
export function createUserConfig(): UserConfigInfo {
 | 
						|
  return {
 | 
						|
    anonymous_coward_name: 'Name of user not set',
 | 
						|
  };
 | 
						|
}
 | 
						|
 | 
						|
export function createServerInfo(): ServerInfo {
 | 
						|
  return {
 | 
						|
    accounts: createAccountsConfig(),
 | 
						|
    auth: createAuth(),
 | 
						|
    change: createChangeConfig(),
 | 
						|
    download: createDownloadInfo(),
 | 
						|
    gerrit: createGerritInfo(),
 | 
						|
    plugin: createPluginConfig(),
 | 
						|
    suggest: createSuggestInfo(),
 | 
						|
    user: createUserConfig(),
 | 
						|
  };
 | 
						|
}
 | 
						|
 | 
						|
export function createGetDiffCommentsOutput(): GetDiffCommentsOutput {
 | 
						|
  return {
 | 
						|
    baseComments: [],
 | 
						|
    comments: [],
 | 
						|
  };
 | 
						|
}
 | 
						|
 | 
						|
export function createMergeable(): MergeableInfo {
 | 
						|
  return {
 | 
						|
    submit_type: SubmitType.MERGE_IF_NECESSARY,
 | 
						|
    mergeable: false,
 | 
						|
  };
 | 
						|
}
 | 
						|
 | 
						|
export function createPreferences(): PreferencesInfo {
 | 
						|
  return {
 | 
						|
    changes_per_page: 10,
 | 
						|
    theme: AppTheme.LIGHT,
 | 
						|
    date_format: DateFormat.ISO,
 | 
						|
    time_format: TimeFormat.HHMM_24,
 | 
						|
    diff_view: DiffViewMode.SIDE_BY_SIDE,
 | 
						|
    my: [],
 | 
						|
    change_table: [],
 | 
						|
    email_strategy: EmailStrategy.ENABLED,
 | 
						|
    default_base_for_merges: DefaultBase.AUTO_MERGE,
 | 
						|
  };
 | 
						|
}
 | 
						|
 | 
						|
export function createApproval(): ApprovalInfo {
 | 
						|
  return createAccountWithId();
 | 
						|
}
 | 
						|
 | 
						|
export function createAppElementChangeViewParams(): AppElementChangeViewParams {
 | 
						|
  return {
 | 
						|
    view: GerritView.CHANGE,
 | 
						|
    changeNum: TEST_NUMERIC_CHANGE_ID,
 | 
						|
    project: TEST_PROJECT_NAME,
 | 
						|
  };
 | 
						|
}
 | 
						|
 | 
						|
export function createRequirement(): Requirement {
 | 
						|
  return {
 | 
						|
    status: RequirementStatus.OK,
 | 
						|
    fallbackText: '',
 | 
						|
    type: 'wip' as RequirementType,
 | 
						|
  };
 | 
						|
}
 | 
						|
 | 
						|
export function createWebLinkInfo(): WebLinkInfo {
 | 
						|
  return {
 | 
						|
    name: 'gitiles',
 | 
						|
    url: '#',
 | 
						|
    image_url: 'gitiles.jpg',
 | 
						|
  };
 | 
						|
}
 | 
						|
 | 
						|
export function createComment(): UIComment {
 | 
						|
  return {
 | 
						|
    patch_set: 1 as PatchSetNum,
 | 
						|
    id: '12345' as UrlEncodedCommentId,
 | 
						|
    side: CommentSide.REVISION,
 | 
						|
    line: 1,
 | 
						|
    message: 'hello world',
 | 
						|
    updated: '2018-02-13 22:48:48.018000000' as Timestamp,
 | 
						|
    unresolved: false,
 | 
						|
  };
 | 
						|
}
 | 
						|
 | 
						|
export function createDraft(): UIDraft {
 | 
						|
  return {
 | 
						|
    ...createComment(),
 | 
						|
    collapsed: false,
 | 
						|
    __draft: true,
 | 
						|
    __editing: false,
 | 
						|
  };
 | 
						|
}
 | 
						|
 | 
						|
export function createChangeComments(): ChangeComments {
 | 
						|
  const comments = {
 | 
						|
    '/COMMIT_MSG': [
 | 
						|
      {
 | 
						|
        ...createComment(),
 | 
						|
        message: 'Done',
 | 
						|
        updated: '2017-02-08 16:40:49' as Timestamp,
 | 
						|
        id: '1' as UrlEncodedCommentId,
 | 
						|
      },
 | 
						|
      {
 | 
						|
        ...createComment(),
 | 
						|
        message: 'oh hay',
 | 
						|
        updated: '2017-02-09 16:40:49' as Timestamp,
 | 
						|
        id: '2' as UrlEncodedCommentId,
 | 
						|
      },
 | 
						|
      {
 | 
						|
        ...createComment(),
 | 
						|
        patch_set: 2 as PatchSetNum,
 | 
						|
        message: 'hello',
 | 
						|
        updated: '2017-02-10 16:40:49' as Timestamp,
 | 
						|
        id: '3' as UrlEncodedCommentId,
 | 
						|
      },
 | 
						|
    ],
 | 
						|
    'myfile.txt': [
 | 
						|
      {
 | 
						|
        ...createComment(),
 | 
						|
        message: 'good news!',
 | 
						|
        updated: '2017-02-08 16:40:49' as Timestamp,
 | 
						|
        id: '4' as UrlEncodedCommentId,
 | 
						|
      },
 | 
						|
      {
 | 
						|
        ...createComment(),
 | 
						|
        patch_set: 2 as PatchSetNum,
 | 
						|
        message: 'wat!?',
 | 
						|
        updated: '2017-02-09 16:40:49' as Timestamp,
 | 
						|
        id: '5' as UrlEncodedCommentId,
 | 
						|
      },
 | 
						|
      {
 | 
						|
        ...createComment(),
 | 
						|
        patch_set: 2 as PatchSetNum,
 | 
						|
        message: 'hi',
 | 
						|
        updated: '2017-02-10 16:40:49' as Timestamp,
 | 
						|
        id: '6' as UrlEncodedCommentId,
 | 
						|
      },
 | 
						|
    ],
 | 
						|
    'unresolved.file': [
 | 
						|
      {
 | 
						|
        ...createComment(),
 | 
						|
        patch_set: 2 as PatchSetNum,
 | 
						|
        message: 'wat!?',
 | 
						|
        updated: '2017-02-09 16:40:49' as Timestamp,
 | 
						|
        id: '7' as UrlEncodedCommentId,
 | 
						|
        unresolved: true,
 | 
						|
      },
 | 
						|
      {
 | 
						|
        ...createComment(),
 | 
						|
        patch_set: 2 as PatchSetNum,
 | 
						|
        message: 'hi',
 | 
						|
        updated: '2017-02-10 16:40:49' as Timestamp,
 | 
						|
        id: '8' as UrlEncodedCommentId,
 | 
						|
        in_reply_to: '7' as UrlEncodedCommentId,
 | 
						|
        unresolved: false,
 | 
						|
      },
 | 
						|
      {
 | 
						|
        ...createComment(),
 | 
						|
        patch_set: 2 as PatchSetNum,
 | 
						|
        message: 'good news!',
 | 
						|
        updated: '2017-02-08 16:40:49' as Timestamp,
 | 
						|
        id: '9' as UrlEncodedCommentId,
 | 
						|
        unresolved: true,
 | 
						|
      },
 | 
						|
    ],
 | 
						|
  };
 | 
						|
  const drafts = {
 | 
						|
    '/COMMIT_MSG': [
 | 
						|
      {
 | 
						|
        ...createDraft(),
 | 
						|
        message: 'hi',
 | 
						|
        updated: '2017-02-15 16:40:49' as Timestamp,
 | 
						|
        id: '10' as UrlEncodedCommentId,
 | 
						|
        unresolved: true,
 | 
						|
      },
 | 
						|
      {
 | 
						|
        ...createDraft(),
 | 
						|
        message: 'fyi',
 | 
						|
        updated: '2017-02-15 16:40:49' as Timestamp,
 | 
						|
        id: '11' as UrlEncodedCommentId,
 | 
						|
        unresolved: false,
 | 
						|
      },
 | 
						|
    ],
 | 
						|
    'unresolved.file': [
 | 
						|
      {
 | 
						|
        ...createDraft(),
 | 
						|
        message: 'hi',
 | 
						|
        updated: '2017-02-11 16:40:49' as Timestamp,
 | 
						|
        id: '12' as UrlEncodedCommentId,
 | 
						|
        unresolved: false,
 | 
						|
      },
 | 
						|
    ],
 | 
						|
  };
 | 
						|
  return new ChangeComments(comments, {}, drafts, {}, {});
 | 
						|
}
 |