/*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ import { Comment, GitHub, GitHubIssue, Issue, Query, User } from './api' type TestbedConfig = { globalLabels: string[] configs: Record writers: string[] releasedCommits: string[] queryRunner: (query: Query) => AsyncIterableIterator<(TestbedIssueConstructorArgs | TestbedIssue)[]> } export type TestbedConstructorArgs = Partial export class Testbed implements GitHub { public config: TestbedConfig constructor(config?: TestbedConstructorArgs) { this.config = { globalLabels: config?.globalLabels ?? [], configs: config?.configs ?? {}, writers: config?.writers ?? [], releasedCommits: config?.releasedCommits ?? [], queryRunner: config?.queryRunner ?? async function* () { yield [] }, } } async *query(query: Query): AsyncIterableIterator { for await (const page of this.config.queryRunner(query)) { yield page.map((issue) => issue instanceof TestbedIssue ? issue : new TestbedIssue(this.config, issue), ) } } async createIssue(_owner: string, _repo: string, _title: string, _body: string): Promise { // pass... } async readConfig(path: string): Promise { return JSON.parse(JSON.stringify(this.config.configs[path])) } async hasWriteAccess(user: User): Promise { return this.config.writers.includes(user.name) } async repoHasLabel(label: string): Promise { return this.config.globalLabels.includes(label) } async createLabel(label: string, _color: string, _description: string): Promise { this.config.globalLabels.push(label) } async deleteLabel(labelToDelete: string): Promise { this.config.globalLabels = this.config.globalLabels.filter((label) => label !== labelToDelete) } async releaseContainsCommit(_release: string, commit: string): Promise { return this.config.releasedCommits.includes(commit) } } type TestbedIssueConfig = { issue: Omit comments: Comment[] labels: string[] closingCommit: { hash: string | undefined; timestamp: number } | undefined } export type TestbedIssueConstructorArgs = Partial> & { issue?: Partial> } export class TestbedIssue extends Testbed implements GitHubIssue { public issueConfig: TestbedIssueConfig constructor(globalConfig?: TestbedConstructorArgs, issueConfig?: TestbedIssueConstructorArgs) { super(globalConfig) issueConfig = issueConfig ?? {} issueConfig.comments = issueConfig?.comments ?? [] issueConfig.labels = issueConfig?.labels ?? [] issueConfig.issue = { author: { name: 'JacksonKearl' }, body: 'issue body', locked: false, numComments: issueConfig?.comments?.length || 0, number: 1, open: true, title: 'issue title', assignee: undefined, reactions: { '+1': 0, '-1': 0, confused: 0, eyes: 0, heart: 0, hooray: 0, laugh: 0, rocket: 0, }, closedAt: undefined, createdAt: +new Date(), updatedAt: +new Date(), ...issueConfig.issue, } this.issueConfig = issueConfig as TestbedIssueConfig } async addAssignee(assignee: string): Promise { this.issueConfig.issue.assignee = assignee } async setMilestone(milestoneId: number): Promise { this.issueConfig.issue.milestoneId = milestoneId } async getIssue(): Promise { const labels = [...this.issueConfig.labels] return { ...this.issueConfig.issue, labels } } async postComment(body: string, author?: string): Promise { this.issueConfig.comments.push({ author: { name: author ?? 'bot' }, body, id: Math.random(), timestamp: +new Date(), }) } async deleteComment(id: number): Promise { this.issueConfig.comments = this.issueConfig.comments.filter((comment) => comment.id !== id) } async *getComments(last?: boolean): AsyncIterableIterator { yield last ? [this.issueConfig.comments[this.issueConfig.comments.length - 1]] : this.issueConfig.comments } async addLabel(label: string): Promise { this.issueConfig.labels.push(label) } async removeLabel(labelToDelete: string): Promise { this.issueConfig.labels = this.issueConfig.labels.filter((label) => label !== labelToDelete) } async closeIssue(): Promise { this.issueConfig.issue.open = false } async lockIssue(): Promise { this.issueConfig.issue.locked = true } async getClosingInfo(): Promise<{ hash: string | undefined; timestamp: number } | undefined> { return this.issueConfig.closingCommit } }