Merge pull request #1572 from github/koesie10/request-repo-results-message
Implement `requestRepoResults` message
This commit is contained in:
@@ -956,6 +956,12 @@ async function activateWithInstalledDistribution(
|
|||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
|
ctx.subscriptions.push(
|
||||||
|
commandRunner('codeQL.loadVariantAnalysisRepoResults', async (variantAnalysisId: number, repositoryFullName: string) => {
|
||||||
|
await variantAnalysisManager.loadResults(variantAnalysisId, repositoryFullName);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
// The "openVariantAnalysisView" command is internal-only.
|
// The "openVariantAnalysisView" command is internal-only.
|
||||||
ctx.subscriptions.push(
|
ctx.subscriptions.push(
|
||||||
commandRunner('codeQL.openVariantAnalysisView', async (variantAnalysisId: number) => {
|
commandRunner('codeQL.openVariantAnalysisView', async (variantAnalysisId: number) => {
|
||||||
|
|||||||
@@ -459,6 +459,11 @@ export interface SetRepoStatesMessage {
|
|||||||
repoStates: VariantAnalysisScannedRepositoryState[];
|
repoStates: VariantAnalysisScannedRepositoryState[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface RequestRepositoryResultsMessage {
|
||||||
|
t: 'requestRepositoryResults';
|
||||||
|
repositoryFullName: string;
|
||||||
|
}
|
||||||
|
|
||||||
export type ToVariantAnalysisMessage =
|
export type ToVariantAnalysisMessage =
|
||||||
| SetVariantAnalysisMessage
|
| SetVariantAnalysisMessage
|
||||||
| SetRepoResultsMessage
|
| SetRepoResultsMessage
|
||||||
@@ -466,4 +471,5 @@ export type ToVariantAnalysisMessage =
|
|||||||
|
|
||||||
export type FromVariantAnalysisMessage =
|
export type FromVariantAnalysisMessage =
|
||||||
| ViewLoadedMsg
|
| ViewLoadedMsg
|
||||||
| StopVariantAnalysisMessage;
|
| StopVariantAnalysisMessage
|
||||||
|
| RequestRepositoryResultsMessage;
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import {
|
|||||||
import {
|
import {
|
||||||
VariantAnalysis,
|
VariantAnalysis,
|
||||||
VariantAnalysisScannedRepositoryDownloadStatus,
|
VariantAnalysisScannedRepositoryDownloadStatus,
|
||||||
|
VariantAnalysisScannedRepositoryResult,
|
||||||
VariantAnalysisScannedRepositoryState
|
VariantAnalysisScannedRepositoryState
|
||||||
} from './shared/variant-analysis';
|
} from './shared/variant-analysis';
|
||||||
import { getErrorMessage } from '../pure/helpers-pure';
|
import { getErrorMessage } from '../pure/helpers-pure';
|
||||||
@@ -26,6 +27,7 @@ export class VariantAnalysisManager extends DisposableObject implements VariantA
|
|||||||
|
|
||||||
private readonly variantAnalysisMonitor: VariantAnalysisMonitor;
|
private readonly variantAnalysisMonitor: VariantAnalysisMonitor;
|
||||||
private readonly variantAnalysisResultsManager: VariantAnalysisResultsManager;
|
private readonly variantAnalysisResultsManager: VariantAnalysisResultsManager;
|
||||||
|
private readonly variantAnalyses = new Map<number, VariantAnalysis>();
|
||||||
private readonly views = new Map<number, VariantAnalysisView>();
|
private readonly views = new Map<number, VariantAnalysisView>();
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@@ -39,6 +41,7 @@ export class VariantAnalysisManager extends DisposableObject implements VariantA
|
|||||||
this.variantAnalysisMonitor.onVariantAnalysisChange(this.onVariantAnalysisUpdated.bind(this));
|
this.variantAnalysisMonitor.onVariantAnalysisChange(this.onVariantAnalysisUpdated.bind(this));
|
||||||
|
|
||||||
this.variantAnalysisResultsManager = this.push(new VariantAnalysisResultsManager(cliServer, storagePath, logger));
|
this.variantAnalysisResultsManager = this.push(new VariantAnalysisResultsManager(cliServer, storagePath, logger));
|
||||||
|
this.variantAnalysisResultsManager.onResultLoaded(this.onRepoResultLoaded.bind(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
public async showView(variantAnalysisId: number): Promise<void> {
|
public async showView(variantAnalysisId: number): Promise<void> {
|
||||||
@@ -68,11 +71,22 @@ export class VariantAnalysisManager extends DisposableObject implements VariantA
|
|||||||
return this.views.get(variantAnalysisId);
|
return this.views.get(variantAnalysisId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async loadResults(variantAnalysisId: number, repositoryFullName: string): Promise<void> {
|
||||||
|
const variantAnalysis = this.variantAnalyses.get(variantAnalysisId);
|
||||||
|
if (!variantAnalysis) {
|
||||||
|
throw new Error(`No variant analysis with id: ${variantAnalysisId}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.variantAnalysisResultsManager.loadResults(variantAnalysisId, repositoryFullName);
|
||||||
|
}
|
||||||
|
|
||||||
private async onVariantAnalysisUpdated(variantAnalysis: VariantAnalysis | undefined): Promise<void> {
|
private async onVariantAnalysisUpdated(variantAnalysis: VariantAnalysis | undefined): Promise<void> {
|
||||||
if (!variantAnalysis) {
|
if (!variantAnalysis) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.variantAnalyses.set(variantAnalysis.id, variantAnalysis);
|
||||||
|
|
||||||
await this.getView(variantAnalysis.id)?.updateView(variantAnalysis);
|
await this.getView(variantAnalysis.id)?.updateView(variantAnalysis);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -80,6 +94,10 @@ export class VariantAnalysisManager extends DisposableObject implements VariantA
|
|||||||
this._onVariantAnalysisAdded.fire(variantAnalysis);
|
this._onVariantAnalysisAdded.fire(variantAnalysis);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async onRepoResultLoaded(repositoryResult: VariantAnalysisScannedRepositoryResult): Promise<void> {
|
||||||
|
await this.getView(repositoryResult.variantAnalysisId)?.sendRepositoryResults([repositoryResult]);
|
||||||
|
}
|
||||||
|
|
||||||
private async onRepoStateUpdated(variantAnalysisId: number, repoState: VariantAnalysisScannedRepositoryState): Promise<void> {
|
private async onRepoStateUpdated(variantAnalysisId: number, repoState: VariantAnalysisScannedRepositoryState): Promise<void> {
|
||||||
await this.getView(variantAnalysisId)?.updateRepoState(repoState);
|
await this.getView(variantAnalysisId)?.updateRepoState(repoState);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,6 +41,8 @@ export class VariantAnalysisMonitor extends DisposableObject {
|
|||||||
let attemptCount = 0;
|
let attemptCount = 0;
|
||||||
const scannedReposDownloaded: number[] = [];
|
const scannedReposDownloaded: number[] = [];
|
||||||
|
|
||||||
|
this._onVariantAnalysisChange.fire(variantAnalysis);
|
||||||
|
|
||||||
while (attemptCount <= VariantAnalysisMonitor.maxAttemptCount) {
|
while (attemptCount <= VariantAnalysisMonitor.maxAttemptCount) {
|
||||||
await this.sleep(VariantAnalysisMonitor.sleepTime);
|
await this.sleep(VariantAnalysisMonitor.sleepTime);
|
||||||
|
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ import { unzipFile } from '../pure/zip';
|
|||||||
|
|
||||||
type CacheKey = `${number}/${string}`;
|
type CacheKey = `${number}/${string}`;
|
||||||
|
|
||||||
const createCacheKey = (variantAnalysisId: number, repoTask: VariantAnalysisRepoTask): CacheKey => `${variantAnalysisId}/${repoTask.repository.full_name}`;
|
const createCacheKey = (variantAnalysisId: number, repositoryFullName: string): CacheKey => `${variantAnalysisId}/${repositoryFullName}`;
|
||||||
|
|
||||||
export type ResultDownloadedEvent = {
|
export type ResultDownloadedEvent = {
|
||||||
variantAnalysisId: number;
|
variantAnalysisId: number;
|
||||||
@@ -26,6 +26,9 @@ export type ResultDownloadedEvent = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class VariantAnalysisResultsManager extends DisposableObject {
|
export class VariantAnalysisResultsManager extends DisposableObject {
|
||||||
|
private static readonly REPO_TASK_FILENAME = 'repo_task.json';
|
||||||
|
private static readonly RESULTS_DIRECTORY = 'results';
|
||||||
|
|
||||||
private readonly cachedResults: Map<CacheKey, VariantAnalysisScannedRepositoryResult>;
|
private readonly cachedResults: Map<CacheKey, VariantAnalysisScannedRepositoryResult>;
|
||||||
|
|
||||||
private readonly _onResultDownloaded = this.push(new EventEmitter<ResultDownloadedEvent>());
|
private readonly _onResultDownloaded = this.push(new EventEmitter<ResultDownloadedEvent>());
|
||||||
@@ -63,8 +66,10 @@ export class VariantAnalysisResultsManager extends DisposableObject {
|
|||||||
await fs.mkdir(resultDirectory, { recursive: true });
|
await fs.mkdir(resultDirectory, { recursive: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
await fs.outputJson(path.join(resultDirectory, VariantAnalysisResultsManager.REPO_TASK_FILENAME), repoTask);
|
||||||
|
|
||||||
const zipFilePath = path.join(resultDirectory, 'results.zip');
|
const zipFilePath = path.join(resultDirectory, 'results.zip');
|
||||||
const unzippedFilesDirectory = path.join(resultDirectory, 'results');
|
const unzippedFilesDirectory = path.join(resultDirectory, VariantAnalysisResultsManager.RESULTS_DIRECTORY);
|
||||||
|
|
||||||
fs.writeFileSync(zipFilePath, Buffer.from(result));
|
fs.writeFileSync(zipFilePath, Buffer.from(result));
|
||||||
await unzipFile(zipFilePath, unzippedFilesDirectory);
|
await unzipFile(zipFilePath, unzippedFilesDirectory);
|
||||||
@@ -77,41 +82,44 @@ export class VariantAnalysisResultsManager extends DisposableObject {
|
|||||||
|
|
||||||
public async loadResults(
|
public async loadResults(
|
||||||
variantAnalysisId: number,
|
variantAnalysisId: number,
|
||||||
repoTask: VariantAnalysisRepoTask
|
repositoryFullName: string
|
||||||
): Promise<VariantAnalysisScannedRepositoryResult> {
|
): Promise<VariantAnalysisScannedRepositoryResult> {
|
||||||
const result = this.cachedResults.get(createCacheKey(variantAnalysisId, repoTask));
|
const result = this.cachedResults.get(createCacheKey(variantAnalysisId, repositoryFullName));
|
||||||
|
|
||||||
return result ?? await this.loadResultsIntoMemory(variantAnalysisId, repoTask);
|
return result ?? await this.loadResultsIntoMemory(variantAnalysisId, repositoryFullName);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async loadResultsIntoMemory(
|
private async loadResultsIntoMemory(
|
||||||
variantAnalysisId: number,
|
variantAnalysisId: number,
|
||||||
repoTask: VariantAnalysisRepoTask,
|
repositoryFullName: string,
|
||||||
): Promise<VariantAnalysisScannedRepositoryResult> {
|
): Promise<VariantAnalysisScannedRepositoryResult> {
|
||||||
const result = await this.loadResultsFromStorage(variantAnalysisId, repoTask);
|
const result = await this.loadResultsFromStorage(variantAnalysisId, repositoryFullName);
|
||||||
this.cachedResults.set(createCacheKey(variantAnalysisId, repoTask), result);
|
this.cachedResults.set(createCacheKey(variantAnalysisId, repositoryFullName), result);
|
||||||
this._onResultLoaded.fire(result);
|
this._onResultLoaded.fire(result);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async loadResultsFromStorage(
|
private async loadResultsFromStorage(
|
||||||
variantAnalysisId: number,
|
variantAnalysisId: number,
|
||||||
repoTask: VariantAnalysisRepoTask,
|
repositoryFullName: string,
|
||||||
): Promise<VariantAnalysisScannedRepositoryResult> {
|
): Promise<VariantAnalysisScannedRepositoryResult> {
|
||||||
if (!(await this.isVariantAnalysisRepoDownloaded(variantAnalysisId, repoTask))) {
|
if (!(await this.isVariantAnalysisRepoDownloaded(variantAnalysisId, repositoryFullName))) {
|
||||||
throw new Error('Variant analysis results not downloaded');
|
throw new Error('Variant analysis results not downloaded');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const storageDirectory = this.getRepoStorageDirectory(variantAnalysisId, repositoryFullName);
|
||||||
|
|
||||||
|
const repoTask: VariantAnalysisRepoTask = await fs.readJson(path.join(storageDirectory, VariantAnalysisResultsManager.REPO_TASK_FILENAME));
|
||||||
|
|
||||||
if (!repoTask.database_commit_sha || !repoTask.source_location_prefix) {
|
if (!repoTask.database_commit_sha || !repoTask.source_location_prefix) {
|
||||||
throw new Error('Missing database commit SHA');
|
throw new Error('Missing database commit SHA');
|
||||||
}
|
}
|
||||||
|
|
||||||
const fileLinkPrefix = this.createGitHubDotcomFileLinkPrefix(repoTask.repository.full_name, repoTask.database_commit_sha);
|
const fileLinkPrefix = this.createGitHubDotcomFileLinkPrefix(repoTask.repository.full_name, repoTask.database_commit_sha);
|
||||||
|
|
||||||
const storageDirectory = this.getRepoStorageDirectory(variantAnalysisId, repoTask.repository.full_name);
|
const resultsDirectory = path.join(storageDirectory, VariantAnalysisResultsManager.RESULTS_DIRECTORY);
|
||||||
|
const sarifPath = path.join(resultsDirectory, 'results.sarif');
|
||||||
const sarifPath = path.join(storageDirectory, 'results.sarif');
|
const bqrsPath = path.join(resultsDirectory, 'results.bqrs');
|
||||||
const bqrsPath = path.join(storageDirectory, 'results.bqrs');
|
|
||||||
if (await fs.pathExists(sarifPath)) {
|
if (await fs.pathExists(sarifPath)) {
|
||||||
const interpretedResults = await this.readSarifResults(sarifPath, fileLinkPrefix);
|
const interpretedResults = await this.readSarifResults(sarifPath, fileLinkPrefix);
|
||||||
|
|
||||||
@@ -137,9 +145,9 @@ export class VariantAnalysisResultsManager extends DisposableObject {
|
|||||||
|
|
||||||
private async isVariantAnalysisRepoDownloaded(
|
private async isVariantAnalysisRepoDownloaded(
|
||||||
variantAnalysisId: number,
|
variantAnalysisId: number,
|
||||||
repoTask: VariantAnalysisRepoTask,
|
repositoryFullName: string,
|
||||||
): Promise<boolean> {
|
): Promise<boolean> {
|
||||||
return await fs.pathExists(this.getRepoStorageDirectory(variantAnalysisId, repoTask.repository.full_name));
|
return await fs.pathExists(this.getRepoStorageDirectory(variantAnalysisId, repositoryFullName));
|
||||||
}
|
}
|
||||||
|
|
||||||
private async readBqrsResults(filePath: string, fileLinkPrefix: string, sourceLocationPrefix: string): Promise<AnalysisRawResults> {
|
private async readBqrsResults(filePath: string, fileLinkPrefix: string, sourceLocationPrefix: string): Promise<AnalysisRawResults> {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { ExtensionContext, ViewColumn } from 'vscode';
|
import { commands, ExtensionContext, ViewColumn } from 'vscode';
|
||||||
import { AbstractWebview, WebviewPanelConfig } from '../abstract-webview';
|
import { AbstractWebview, WebviewPanelConfig } from '../abstract-webview';
|
||||||
import { logger } from '../logging';
|
import { logger } from '../logging';
|
||||||
import { FromVariantAnalysisMessage, ToVariantAnalysisMessage } from '../pure/interface-types';
|
import { FromVariantAnalysisMessage, ToVariantAnalysisMessage } from '../pure/interface-types';
|
||||||
@@ -7,6 +7,7 @@ import {
|
|||||||
VariantAnalysis,
|
VariantAnalysis,
|
||||||
VariantAnalysisQueryLanguage,
|
VariantAnalysisQueryLanguage,
|
||||||
VariantAnalysisRepoStatus,
|
VariantAnalysisRepoStatus,
|
||||||
|
VariantAnalysisScannedRepositoryResult,
|
||||||
VariantAnalysisScannedRepositoryState,
|
VariantAnalysisScannedRepositoryState,
|
||||||
VariantAnalysisStatus
|
VariantAnalysisStatus
|
||||||
} from './shared/variant-analysis';
|
} from './shared/variant-analysis';
|
||||||
@@ -53,6 +54,17 @@ export class VariantAnalysisView extends AbstractWebview<ToVariantAnalysisMessag
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async sendRepositoryResults(repositoryResult: VariantAnalysisScannedRepositoryResult[]): Promise<void> {
|
||||||
|
if (!this.isShowingPanel) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.postMessage({
|
||||||
|
t: 'setRepoResults',
|
||||||
|
repoResults: repositoryResult,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
protected getPanelConfig(): WebviewPanelConfig {
|
protected getPanelConfig(): WebviewPanelConfig {
|
||||||
return {
|
return {
|
||||||
viewId: VariantAnalysisView.viewType,
|
viewId: VariantAnalysisView.viewType,
|
||||||
@@ -83,6 +95,9 @@ export class VariantAnalysisView extends AbstractWebview<ToVariantAnalysisMessag
|
|||||||
case 'stopVariantAnalysis':
|
case 'stopVariantAnalysis':
|
||||||
void logger.log(`Stop variant analysis: ${msg.variantAnalysisId}`);
|
void logger.log(`Stop variant analysis: ${msg.variantAnalysisId}`);
|
||||||
break;
|
break;
|
||||||
|
case 'requestRepositoryResults':
|
||||||
|
void commands.executeCommand('codeQL.loadVariantAnalysisRepoResults', this.variantAnalysisId, msg.repositoryFullName);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
assertNever(msg);
|
assertNever(msg);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { useCallback, useState } from 'react';
|
import { useCallback, useEffect, useState } from 'react';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
import { VSCodeBadge, VSCodeCheckbox } from '@vscode/webview-ui-toolkit/react';
|
import { VSCodeBadge, VSCodeCheckbox } from '@vscode/webview-ui-toolkit/react';
|
||||||
import {
|
import {
|
||||||
@@ -11,6 +11,7 @@ import { formatDecimal } from '../../pure/number';
|
|||||||
import { Codicon, ErrorIcon, LoadingIcon, SuccessIcon, WarningIcon } from '../common';
|
import { Codicon, ErrorIcon, LoadingIcon, SuccessIcon, WarningIcon } from '../common';
|
||||||
import { Repository } from '../../remote-queries/shared/repository';
|
import { Repository } from '../../remote-queries/shared/repository';
|
||||||
import { AnalysisAlert, AnalysisRawResults } from '../../remote-queries/shared/analysis-result';
|
import { AnalysisAlert, AnalysisRawResults } from '../../remote-queries/shared/analysis-result';
|
||||||
|
import { vscode } from '../vscode-api';
|
||||||
import { AnalyzedRepoItemContent } from './AnalyzedRepoItemContent';
|
import { AnalyzedRepoItemContent } from './AnalyzedRepoItemContent';
|
||||||
|
|
||||||
// This will ensure that these icons have a className which we can use in the TitleContainer
|
// This will ensure that these icons have a className which we can use in the TitleContainer
|
||||||
@@ -82,12 +83,36 @@ export const RepoRow = ({
|
|||||||
rawResults,
|
rawResults,
|
||||||
}: RepoRowProps) => {
|
}: RepoRowProps) => {
|
||||||
const [isExpanded, setExpanded] = useState(false);
|
const [isExpanded, setExpanded] = useState(false);
|
||||||
|
const resultsLoaded = !!interpretedResults || !!rawResults;
|
||||||
|
const [resultsLoading, setResultsLoading] = useState(false);
|
||||||
|
|
||||||
const toggleExpanded = useCallback(() => {
|
const toggleExpanded = useCallback(async () => {
|
||||||
setExpanded(oldIsExpanded => !oldIsExpanded);
|
if (resultsLoading) {
|
||||||
}, []);
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (resultsLoaded || status !== VariantAnalysisRepoStatus.Succeeded) {
|
||||||
|
setExpanded(oldIsExpanded => !oldIsExpanded);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
vscode.postMessage({
|
||||||
|
t: 'requestRepositoryResults',
|
||||||
|
repositoryFullName: repository.fullName,
|
||||||
|
});
|
||||||
|
|
||||||
|
setResultsLoading(true);
|
||||||
|
}, [resultsLoading, resultsLoaded, repository.fullName, status]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (resultsLoaded && resultsLoading) {
|
||||||
|
setResultsLoading(false);
|
||||||
|
setExpanded(true);
|
||||||
|
}
|
||||||
|
}, [resultsLoaded, resultsLoading]);
|
||||||
|
|
||||||
const disabled = !status || !isCompletedAnalysisRepoStatus(status);
|
const disabled = !status || !isCompletedAnalysisRepoStatus(status);
|
||||||
|
const expandableContentLoaded = status && (status !== VariantAnalysisRepoStatus.Succeeded || resultsLoaded);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
@@ -107,7 +132,7 @@ export const RepoRow = ({
|
|||||||
</span>
|
</span>
|
||||||
{downloadStatus === VariantAnalysisScannedRepositoryDownloadStatus.InProgress && <LoadingIcon label="Downloading" />}
|
{downloadStatus === VariantAnalysisScannedRepositoryDownloadStatus.InProgress && <LoadingIcon label="Downloading" />}
|
||||||
</TitleContainer>
|
</TitleContainer>
|
||||||
{isExpanded && status &&
|
{isExpanded && expandableContentLoaded &&
|
||||||
<AnalyzedRepoItemContent status={status} interpretedResults={interpretedResults} rawResults={rawResults} />}
|
<AnalyzedRepoItemContent status={status} interpretedResults={interpretedResults} rawResults={rawResults} />}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -164,6 +164,56 @@ describe(RepoRow.name, () => {
|
|||||||
screen.getByText('Error: Timed out');
|
screen.getByText('Error: Timed out');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('can expand the repo item when succeeded and loaded', async () => {
|
||||||
|
render({
|
||||||
|
status: VariantAnalysisRepoStatus.Succeeded,
|
||||||
|
interpretedResults: [],
|
||||||
|
});
|
||||||
|
|
||||||
|
await userEvent.click(screen.getByRole('button', {
|
||||||
|
expanded: false
|
||||||
|
}));
|
||||||
|
|
||||||
|
expect(screen.getByRole('button', {
|
||||||
|
expanded: true,
|
||||||
|
})).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('can expand the repo item when succeeded and not loaded', async () => {
|
||||||
|
const { rerender } = render({
|
||||||
|
status: VariantAnalysisRepoStatus.Succeeded,
|
||||||
|
});
|
||||||
|
|
||||||
|
await userEvent.click(screen.getByRole('button', {
|
||||||
|
expanded: false
|
||||||
|
}));
|
||||||
|
|
||||||
|
expect((window as any).vsCodeApi.postMessage).toHaveBeenCalledWith({
|
||||||
|
t: 'requestRepositoryResults',
|
||||||
|
repositoryFullName: 'octodemo/hello-world-1',
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(screen.getByRole('button', {
|
||||||
|
expanded: false,
|
||||||
|
})).toBeInTheDocument();
|
||||||
|
|
||||||
|
rerender(
|
||||||
|
<RepoRow
|
||||||
|
repository={{
|
||||||
|
id: 1,
|
||||||
|
fullName: 'octodemo/hello-world-1',
|
||||||
|
private: false,
|
||||||
|
}}
|
||||||
|
status={VariantAnalysisRepoStatus.Succeeded}
|
||||||
|
interpretedResults={[]}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(screen.getByRole('button', {
|
||||||
|
expanded: true,
|
||||||
|
})).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
it('does not allow expanding the repo item when status is undefined', async () => {
|
it('does not allow expanding the repo item when status is undefined', async () => {
|
||||||
render({
|
render({
|
||||||
status: undefined,
|
status: undefined,
|
||||||
|
|||||||
@@ -14,3 +14,11 @@ Object.defineProperty(window, 'matchMedia', {
|
|||||||
dispatchEvent: jest.fn(),
|
dispatchEvent: jest.fn(),
|
||||||
})),
|
})),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Store this on the window so we can mock it
|
||||||
|
(window as any).vsCodeApi = {
|
||||||
|
postMessage: jest.fn(),
|
||||||
|
setState: jest.fn(),
|
||||||
|
};
|
||||||
|
|
||||||
|
(window as any).acquireVsCodeApi = () => (window as any).vsCodeApi;
|
||||||
|
|||||||
Reference in New Issue
Block a user