Merge branch 'main' into robertbrignull/always_trigger_monitoring

This commit is contained in:
Robert
2022-10-31 15:42:52 +00:00
31 changed files with 245 additions and 57 deletions

View File

@@ -0,0 +1,6 @@
.vscode-test/
node_modules/
out/
# Include the Storybook config
!.storybook

View File

@@ -3,7 +3,7 @@ module.exports = {
parserOptions: {
ecmaVersion: 2018,
sourceType: "module",
project: ["tsconfig.json", "./src/**/tsconfig.json", "./gulpfile.ts/tsconfig.json"],
project: ["tsconfig.json", "./src/**/tsconfig.json", "./gulpfile.ts/tsconfig.json", "./scripts/tsconfig.json", "./.storybook/tsconfig.json"],
},
plugins: ["@typescript-eslint"],
env: {

View File

@@ -7,7 +7,7 @@ import '@vscode/codicons/dist/codicon.css';
// https://storybook.js.org/docs/react/configure/overview#configure-story-rendering
export const parameters = {
// All props starting with `on` will automatically receive an action as a prop
actions: { argTypesRegex: "^on[A-Z].*" },
actions: { argTypesRegex: '^on[A-Z].*' },
// All props matching these names will automatically get the correct control
controls: {
matchers: {

View File

@@ -1,7 +1,7 @@
export function config(entry = []) {
return [...entry, require.resolve("./preview.ts")];
return [...entry, require.resolve('./preview.ts')];
}
export function managerEntries(entry = []) {
return [...entry, require.resolve("./manager.tsx")];
return [...entry, require.resolve('./manager.tsx')];
}

View File

@@ -6,4 +6,4 @@ export enum VSCodeTheme {
export const themeNames: { [key in VSCodeTheme]: string } = {
[VSCodeTheme.Dark]: 'Dark+',
[VSCodeTheme.Light]: 'Light+',
}
};

View File

@@ -4,9 +4,11 @@ import type { AnyFramework, PartialStoryFn as StoryFunction, StoryContext } from
import { VSCodeTheme } from './theme';
const themeFiles: { [key in VSCodeTheme]: string } = {
// eslint-disable-next-line @typescript-eslint/no-var-requires
[VSCodeTheme.Dark]: require('!file-loader?modules!../../src/stories/vscode-theme-dark.css').default,
// eslint-disable-next-line @typescript-eslint/no-var-requires
[VSCodeTheme.Light]: require('!file-loader?modules!../../src/stories/vscode-theme-light.css').default,
}
};
export const withTheme = (
StoryFn: StoryFunction<AnyFramework>,
@@ -18,7 +20,7 @@ export const withTheme = (
const styleSelectorId =
context.viewMode === 'docs'
? `addon-vscode-theme-docs-${context.id}`
: `addon-vscode-theme-theme`;
: 'addon-vscode-theme-theme';
const theme = Object.values(VSCodeTheme).includes(vscodeTheme) ? vscodeTheme as VSCodeTheme : VSCodeTheme.Dark;

View File

@@ -37,6 +37,7 @@
"onLanguage:ql",
"onLanguage:ql-summary",
"onView:codeQLDatabases",
"onView:codeQLDatabasesExperimental",
"onView:codeQLQueryHistory",
"onView:codeQLAstViewer",
"onView:codeQLEvalLogViewer",
@@ -1208,6 +1209,11 @@
"id": "codeQLDatabases",
"name": "Databases"
},
{
"id": "codeQLDatabasesExperimental",
"name": "Databases",
"when": "config.codeQL.canary && config.codeQL.newQueryRunExperience"
},
{
"id": "codeQLQueryHistory",
"name": "Query History"
@@ -1254,8 +1260,8 @@
"integration": "node ./out/vscode-tests/run-integration-tests.js no-workspace,minimal-workspace",
"cli-integration": "npm run preintegration && node ./out/vscode-tests/run-integration-tests.js cli-integration",
"update-vscode": "node ./node_modules/vscode/bin/install",
"format": "tsfmt -r && eslint src test --ext .ts,.tsx --fix",
"lint": "eslint src test --ext .ts,.tsx --max-warnings=0",
"format": "tsfmt -r && eslint . --ext .ts,.tsx --fix",
"lint": "eslint . --ext .ts,.tsx --max-warnings=0",
"format-staged": "lint-staged",
"storybook": "start-storybook -p 6006",
"build-storybook": "build-storybook",

View File

@@ -0,0 +1,6 @@
{
"$schema": "https://json.schemastore.org/tsconfig",
"extends": "../tsconfig.json",
"include": ["**/*.ts"],
"exclude": []
}

View File

@@ -5,7 +5,7 @@ import {
ViewColumn,
Uri,
WebviewPanelOptions,
WebviewOptions
WebviewOptions,
} from 'vscode';
import * as path from 'path';
@@ -27,6 +27,8 @@ export abstract class AbstractWebview<ToMessage extends WebviewMessage, FromMess
protected panelLoaded = false;
protected panelLoadedCallBacks: (() => void)[] = [];
private panelResolves?: Array<(panel: WebviewPanel) => void>;
constructor(
protected readonly ctx: ExtensionContext
) {
@@ -35,20 +37,36 @@ export abstract class AbstractWebview<ToMessage extends WebviewMessage, FromMess
public async restoreView(panel: WebviewPanel): Promise<void> {
this.panel = panel;
this.setupPanel(panel);
const config = await this.getPanelConfig();
this.setupPanel(panel, config);
}
protected get isShowingPanel() {
return !!this.panel;
}
protected getPanel(): WebviewPanel {
protected async getPanel(): Promise<WebviewPanel> {
if (this.panel == undefined) {
const { ctx } = this;
const config = this.getPanelConfig();
// This is an async method, so in theory this method can be called concurrently. To ensure that we don't
// create two panels, we use a promise that resolves when the panel is created. This way, if the panel is
// being created, the promise will resolve when it is done.
if (this.panelResolves !== undefined) {
return new Promise((resolve) => {
if (this.panel !== undefined) {
resolve(this.panel);
return;
}
this.panel = Window.createWebviewPanel(
this.panelResolves?.push(resolve);
});
}
this.panelResolves = [];
const config = await this.getPanelConfig();
const panel = Window.createWebviewPanel(
config.viewId,
config.title,
{ viewColumn: config.viewColumn, preserveFocus: config.preserveFocus },
@@ -64,14 +82,17 @@ export abstract class AbstractWebview<ToMessage extends WebviewMessage, FromMess
],
}
);
this.setupPanel(this.panel);
this.panel = panel;
this.setupPanel(panel, config);
this.panelResolves.forEach((resolve) => resolve(panel));
this.panelResolves = undefined;
}
return this.panel;
}
protected setupPanel(panel: WebviewPanel): void {
const config = this.getPanelConfig();
protected setupPanel(panel: WebviewPanel, config: WebviewPanelConfig): void {
this.push(
panel.onDidDispose(
() => {
@@ -101,7 +122,7 @@ export abstract class AbstractWebview<ToMessage extends WebviewMessage, FromMess
);
}
protected abstract getPanelConfig(): WebviewPanelConfig;
protected abstract getPanelConfig(): WebviewPanelConfig | Promise<WebviewPanelConfig>;
protected abstract onPanelDispose(): void;
@@ -123,8 +144,9 @@ export abstract class AbstractWebview<ToMessage extends WebviewMessage, FromMess
this.panelLoadedCallBacks = [];
}
protected postMessage(msg: ToMessage): Thenable<boolean> {
return this.getPanel().webview.postMessage(msg);
protected async postMessage(msg: ToMessage): Promise<boolean> {
const panel = await this.getPanel();
return panel.webview.postMessage(msg);
}
public dispose(disposeHandler?: DisposeHandler) {

View File

@@ -46,7 +46,8 @@ export class CompareView extends AbstractWebview<ToCompareViewMessage, FromCompa
selectedResultSetName?: string
) {
this.comparePair = { from, to };
this.getPanel().reveal(undefined, true);
const panel = await this.getPanel();
panel.reveal(undefined, true);
await this.waitForPanelLoaded();
const [

View File

@@ -438,6 +438,16 @@ export function isVariantAnalysisLiveResultsEnabled(): boolean {
return !!LIVE_RESULTS.getValue<boolean>();
}
/**
* A flag indicating whether to use the new query run experience which involves
* using a new database panel.
*/
const NEW_QUERY_RUN_EXPERIENCE = new Setting('newQueryRunExperience', ROOT_SETTING);
export function isNewQueryRunExperienceEnabled(): boolean {
return !!NEW_QUERY_RUN_EXPERIENCE.getValue<boolean>();
}
// Settings for mocking the GitHub API.
const MOCK_GH_API_SERVER = new Setting('mockGitHubApiServer', ROOT_SETTING);

View File

@@ -7,7 +7,7 @@ import {
languages,
Uri,
window as Window,
env
env, WebviewPanel
} from 'vscode';
import * as cli from './cli';
import { CodeQLCliServer } from './cli';
@@ -341,6 +341,8 @@ export class ResultsView extends AbstractWebview<IntoResultsViewMsg, FromResults
return;
}
const panel = await this.getPanel();
this._interpretation = undefined;
const interpretationPage = await this.interpretResultsInfo(
fullQuery.completedQuery.query,
@@ -350,12 +352,11 @@ export class ResultsView extends AbstractWebview<IntoResultsViewMsg, FromResults
const sortedResultsMap: SortedResultsMap = {};
Object.entries(fullQuery.completedQuery.sortedResultsInfo).forEach(
([k, v]) =>
(sortedResultsMap[k] = this.convertPathPropertiesToWebviewUris(v))
(sortedResultsMap[k] = this.convertPathPropertiesToWebviewUris(panel, v))
);
this._displayedQuery = fullQuery;
const panel = this.getPanel();
await this.waitForPanelLoaded();
if (!panel.visible) {
if (forceReveal === WebviewReveal.Forced) {
@@ -426,6 +427,7 @@ export class ResultsView extends AbstractWebview<IntoResultsViewMsg, FromResults
interpretation: interpretationPage,
origResultsPaths: fullQuery.completedQuery.query.resultsPaths,
resultsPath: this.convertPathToWebviewUri(
panel,
fullQuery.completedQuery.query.resultsPaths.resultsPath
),
parsedResultSets,
@@ -498,10 +500,12 @@ export class ResultsView extends AbstractWebview<IntoResultsViewMsg, FromResults
throw new Error('trying to view a page of a query that is not loaded');
}
const panel = await this.getPanel();
const sortedResultsMap: SortedResultsMap = {};
Object.entries(results.completedQuery.sortedResultsInfo).forEach(
([k, v]) =>
(sortedResultsMap[k] = this.convertPathPropertiesToWebviewUris(v))
(sortedResultsMap[k] = this.convertPathPropertiesToWebviewUris(panel, v))
);
const resultSetSchemas = await this.getResultSetSchemas(results.completedQuery, sorted ? selectedTable : '');
@@ -544,6 +548,7 @@ export class ResultsView extends AbstractWebview<IntoResultsViewMsg, FromResults
interpretation: this._interpretation,
origResultsPaths: results.completedQuery.query.resultsPaths,
resultsPath: this.convertPathToWebviewUri(
panel,
results.completedQuery.query.resultsPaths.resultsPath
),
parsedResultSets,
@@ -812,15 +817,16 @@ export class ResultsView extends AbstractWebview<IntoResultsViewMsg, FromResults
this._diagnosticCollection.set(diagnostics);
}
private convertPathToWebviewUri(path: string): string {
return fileUriToWebviewUri(this.getPanel(), Uri.file(path));
private convertPathToWebviewUri(panel: WebviewPanel, path: string): string {
return fileUriToWebviewUri(panel, Uri.file(path));
}
private convertPathPropertiesToWebviewUris(
panel: WebviewPanel,
info: SortedResultSetInfo
): SortedResultSetInfo {
return {
resultsPath: this.convertPathToWebviewUri(info.resultsPath),
resultsPath: this.convertPathToWebviewUri(panel, info.resultsPath),
sortState: info.sortState,
};
}

View File

@@ -477,6 +477,10 @@ export interface OpenQueryTextMessage {
t: 'openQueryText';
}
export interface OpenLogsMessage {
t: 'openLogs';
}
export type ToVariantAnalysisMessage =
| SetVariantAnalysisMessage
| SetRepoResultsMessage
@@ -487,4 +491,5 @@ export type FromVariantAnalysisMessage =
| StopVariantAnalysisMessage
| RequestRepositoryResultsMessage
| OpenQueryFileMessage
| OpenQueryTextMessage;
| OpenQueryTextMessage
| OpenLogsMessage;

View File

@@ -3,7 +3,10 @@ import { VariantAnalysisHistoryItem } from './remote-queries/variant-analysis-hi
import { LocalQueryInfo } from './query-results';
import { assertNever } from './pure/helpers-pure';
import { pluralize } from './pure/word';
import { hasRepoScanCompleted } from './remote-queries/shared/variant-analysis';
import {
hasRepoScanCompleted,
getActionsWorkflowRunUrl as getVariantAnalysisActionsWorkflowRunUrl
} from './remote-queries/shared/variant-analysis';
export type QueryHistoryInfo = LocalQueryInfo | RemoteQueryHistoryItem | VariantAnalysisHistoryItem;
@@ -71,3 +74,14 @@ export function buildRepoLabel(item: RemoteQueryHistoryItem | VariantAnalysisHis
assertNever(item);
}
}
export function getActionsWorkflowRunUrl(item: RemoteQueryHistoryItem | VariantAnalysisHistoryItem): string {
if (item.t === 'remote') {
const { actionsWorkflowRunId: workflowRunId, controllerRepository: { owner, name } } = item.remoteQuery;
return `https://github.com/${owner}/${name}/actions/runs/${workflowRunId}`;
} else if (item.t === 'variant-analysis') {
return getVariantAnalysisActionsWorkflowRunUrl(item.variantAnalysis);
} else {
assertNever(item);
}
}

View File

@@ -31,7 +31,7 @@ import { commandRunner } from './commandRunner';
import { ONE_HOUR_IN_MS, TWO_HOURS_IN_MS } from './pure/time';
import { assertNever, getErrorMessage, getErrorStack } from './pure/helpers-pure';
import { CompletedLocalQueryInfo, LocalQueryInfo } from './query-results';
import { getQueryId, getQueryText, QueryHistoryInfo } from './query-history-info';
import { getActionsWorkflowRunUrl, getQueryId, getQueryText, QueryHistoryInfo } from './query-history-info';
import { DatabaseManager } from './databases';
import { registerQueryHistoryScrubber } from './query-history-scrubber';
import { QueryStatus, variantAnalysisStatusToQueryStatus } from './query-status';
@@ -1213,17 +1213,17 @@ export class QueryHistoryManager extends DisposableObject {
) {
const { finalSingleItem, finalMultiSelect } = this.determineSelection(singleItem, multiSelect);
// Remote queries only
if (!this.assertSingleQuery(finalMultiSelect) || !finalSingleItem || finalSingleItem.t !== 'remote') {
if (!this.assertSingleQuery(finalMultiSelect) || !finalSingleItem) {
return;
}
const { actionsWorkflowRunId: workflowRunId, controllerRepository: { owner, name } } = finalSingleItem.remoteQuery;
if (finalSingleItem.t === 'local') {
return;
}
await commands.executeCommand(
'vscode.open',
Uri.parse(`https://github.com/${owner}/${name}/actions/runs/${workflowRunId}`)
);
const actionsWorkflowRunUrl = getActionsWorkflowRunUrl(finalSingleItem);
await commands.executeCommand('vscode.open', Uri.parse(actionsWorkflowRunUrl));
}
async handleCopyRepoList(

View File

@@ -49,7 +49,8 @@ export class RemoteQueriesView extends AbstractWebview<ToRemoteQueriesMessage, F
}
async showResults(query: RemoteQuery, queryResult: RemoteQueryResult) {
this.getPanel().reveal(undefined, true);
const panel = await this.getPanel();
panel.reveal(undefined, true);
await this.waitForPanelLoaded();
const model = this.buildViewModel(query, queryResult);

View File

@@ -3,7 +3,7 @@ import { AnalysisAlert, AnalysisRawResults } from './analysis-result';
export interface VariantAnalysis {
id: number,
controllerRepoId: number,
controllerRepo: Repository;
query: {
name: string,
filePath: string,
@@ -203,3 +203,8 @@ export function getSkippedRepoCount(skippedRepos: VariantAnalysisSkippedReposito
return Object.values(skippedRepos).reduce((acc, group) => acc + group.repositoryCount, 0);
}
export function getActionsWorkflowRunUrl(variantAnalysis: VariantAnalysis): string {
const { actionsWorkflowRunId, controllerRepo: { fullName } } = variantAnalysis;
return `https://github.com/${fullName}/actions/runs/${actionsWorkflowRunId}`;
}

View File

@@ -51,7 +51,7 @@ export class VariantAnalysisMonitor extends DisposableObject {
variantAnalysisSummary = await ghApiClient.getVariantAnalysis(
credentials,
variantAnalysis.controllerRepoId,
variantAnalysis.controllerRepo.id,
variantAnalysis.id
);

View File

@@ -52,7 +52,11 @@ export function processUpdatedVariantAnalysis(
const variantAnalysis: VariantAnalysis = {
id: response.id,
controllerRepoId: response.controller_repo.id,
controllerRepo: {
id: response.controller_repo.id,
fullName: response.controller_repo.full_name,
private: response.controller_repo.private,
},
query: previousVariantAnalysis.query,
databases: previousVariantAnalysis.databases,
executionStartTime: previousVariantAnalysis.executionStartTime,

View File

@@ -5,6 +5,7 @@ import { logger } from '../logging';
import { FromVariantAnalysisMessage, ToVariantAnalysisMessage } from '../pure/interface-types';
import { assertNever } from '../pure/helpers-pure';
import {
getActionsWorkflowRunUrl,
VariantAnalysis,
VariantAnalysisScannedRepositoryResult,
VariantAnalysisScannedRepositoryState,
@@ -26,7 +27,8 @@ export class VariantAnalysisView extends AbstractWebview<ToVariantAnalysisMessag
}
public async openView() {
this.getPanel().reveal(undefined, true);
const panel = await this.getPanel();
panel.reveal(undefined, true);
await this.waitForPanelLoaded();
}
@@ -40,6 +42,9 @@ export class VariantAnalysisView extends AbstractWebview<ToVariantAnalysisMessag
t: 'setVariantAnalysis',
variantAnalysis,
});
const panel = await this.getPanel();
panel.title = `${variantAnalysis.query.name} - CodeQL Query Results`;
}
public async updateRepoState(repoState: VariantAnalysisScannedRepositoryState): Promise<void> {
@@ -64,10 +69,12 @@ export class VariantAnalysisView extends AbstractWebview<ToVariantAnalysisMessag
});
}
protected getPanelConfig(): WebviewPanelConfig {
protected async getPanelConfig(): Promise<WebviewPanelConfig> {
const variantAnalysis = await this.manager.getVariantAnalysis(this.variantAnalysisId);
return {
viewId: VariantAnalysisView.viewType,
title: `CodeQL Query Results for ${this.variantAnalysisId}`,
title: variantAnalysis ? `${variantAnalysis.query.name} - CodeQL Query Results` : `Variant analysis ${this.variantAnalysisId} - CodeQL Query Results`,
viewColumn: ViewColumn.Active,
preserveFocus: true,
view: 'variant-analysis',
@@ -96,6 +103,9 @@ export class VariantAnalysisView extends AbstractWebview<ToVariantAnalysisMessag
case 'openQueryText':
await this.openQueryText();
break;
case 'openLogs':
await this.openLogs();
break;
default:
assertNever(msg);
}
@@ -159,4 +169,16 @@ export class VariantAnalysisView extends AbstractWebview<ToVariantAnalysisMessag
void showAndLogWarningMessage('Could not open variant analysis query text. Failed to open text document.');
}
}
private async openLogs(): Promise<void> {
const variantAnalysis = await this.manager.getVariantAnalysis(this.variantAnalysisId);
if (!variantAnalysis) {
void showAndLogWarningMessage('Could not open variant analysis logs. Variant analysis not found.');
return;
}
const actionsWorkflowRunUrl = getActionsWorkflowRunUrl(variantAnalysis);
await commands.executeCommand('vscode.open', Uri.parse(actionsWorkflowRunUrl));
}
}

View File

@@ -24,7 +24,11 @@ const Template: ComponentStory<typeof VariantAnalysisComponent> = (args) => (
const variantAnalysis: VariantAnalysisDomainModel = {
id: 1,
controllerRepoId: 1,
controllerRepo: {
id: 1,
fullName: 'octodemo/variant-analysis-controller',
private: false,
},
actionsWorkflowRunId: 789263,
query: {
name: 'Example query',

View File

@@ -37,7 +37,11 @@ export const Example = Template.bind({});
Example.args = {
variantAnalysis: {
id: 1,
controllerRepoId: 1,
controllerRepo: {
id: 1,
fullName: 'octodemo/variant-analysis-controller',
private: false,
},
query: {
name: 'Query name',
filePath: 'example.ql',

View File

@@ -68,7 +68,11 @@ const Template: ComponentStory<typeof VariantAnalysisHeader> = (args) => (
const buildVariantAnalysis = (data: Partial<VariantAnalysis>) => ({
id: 1,
controllerRepoId: 1,
controllerRepo: {
id: 1,
fullName: 'octodemo/variant-analysis-controller',
private: false,
},
query: {
name: 'Query name',
filePath: 'example.ql',

View File

@@ -30,7 +30,11 @@ const Template: ComponentStory<typeof VariantAnalysisOutcomePanels> = (args) =>
const buildVariantAnalysis = (data: Partial<VariantAnalysis>) => ({
id: 1,
controllerRepoId: 1,
controllerRepo: {
id: 1,
fullName: 'octodemo/variant-analysis-controller',
private: false,
},
query: {
name: 'Query name',
filePath: 'example.ql',

View File

@@ -30,6 +30,12 @@ const openQueryText = () => {
});
};
const openLogs = () => {
vscode.postMessage({
t: 'openLogs',
});
};
export function VariantAnalysis({
variantAnalysis: initialVariantAnalysis,
repoStates: initialRepoStates = [],
@@ -85,7 +91,7 @@ export function VariantAnalysis({
onStopQueryClick={() => console.log('Stop query')}
onCopyRepositoryListClick={() => console.log('Copy repository list')}
onExportResultsClick={() => console.log('Export results')}
onViewLogsClick={() => console.log('View logs')}
onViewLogsClick={openLogs}
/>
<VariantAnalysisOutcomePanels
variantAnalysis={variantAnalysis}

View File

@@ -11,7 +11,11 @@ import { VariantAnalysisAnalyzedRepos, VariantAnalysisAnalyzedReposProps } from
describe(VariantAnalysisAnalyzedRepos.name, () => {
const defaultVariantAnalysis = {
id: 1,
controllerRepoId: 1,
controllerRepo: {
id: 1,
fullName: 'octodemo/variant-analysis-controller',
private: false,
},
actionsWorkflowRunId: 789263,
query: {
name: 'Example query',

View File

@@ -10,7 +10,11 @@ import { VariantAnalysisOutcomePanelProps, VariantAnalysisOutcomePanels } from '
describe(VariantAnalysisOutcomePanels.name, () => {
const defaultVariantAnalysis = {
id: 1,
controllerRepoId: 1,
controllerRepo: {
id: 1,
fullName: 'octodemo/variant-analysis-controller',
private: false,
},
actionsWorkflowRunId: 789263,
query: {
name: 'Example query',

View File

@@ -26,7 +26,11 @@ describe('Variant Analysis processor', function() {
expect(result).to.eql({
'id': mockApiResponse.id,
'controllerRepoId': mockApiResponse.controller_repo.id,
'controllerRepo': {
'id': mockApiResponse.controller_repo.id,
'fullName': mockApiResponse.controller_repo.full_name,
'private': mockApiResponse.controller_repo.private
},
'query': {
'filePath': 'query-file-path',
'language': VariantAnalysisQueryLanguage.Javascript,

View File

@@ -16,7 +16,13 @@ export function createMockVariantAnalysis(
): VariantAnalysis {
const variantAnalysis: VariantAnalysis = {
id: faker.datatype.number(),
controllerRepoId: faker.datatype.number(),
controllerRepo: {
id: faker.datatype.number(),
fullName: 'github/' + faker.datatype.hexadecimal({
prefix: '',
}),
private: faker.datatype.boolean(),
},
query: {
name: 'a-query-name',
filePath: 'a-query-file-path',

View File

@@ -1,7 +1,13 @@
import { expect } from 'chai';
import { QueryStatus } from '../../src/query-status';
import { buildRepoLabel, getQueryId, getQueryText, getRawQueryName } from '../../src/query-history-info';
import {
buildRepoLabel,
getActionsWorkflowRunUrl,
getQueryId,
getQueryText,
getRawQueryName
} from '../../src/query-history-info';
import { VariantAnalysisHistoryItem } from '../../src/remote-queries/variant-analysis-history-item';
import { createMockVariantAnalysis } from '../../src/vscode-tests/factories/remote-queries/shared/variant-analysis';
import { createMockScannedRepos } from '../../src/vscode-tests/factories/remote-queries/shared/scanned-repositories';
@@ -144,4 +150,26 @@ describe('Query history info', () => {
});
});
});
describe('getActionsWorkflowRunUrl', () => {
it('should get the run url for remote query history items', () => {
const actionsWorkflowRunUrl = getActionsWorkflowRunUrl(remoteQueryHistoryItem);
const remoteQuery = remoteQueryHistoryItem.remoteQuery;
const fullName = `${remoteQuery.controllerRepository.owner}/${remoteQuery.controllerRepository.name}`;
expect(actionsWorkflowRunUrl).to.equal(
`https://github.com/${fullName}/actions/runs/${remoteQuery.actionsWorkflowRunId}`
);
});
it('should get the run url for variant analysis history items', () => {
const actionsWorkflowRunUrl = getActionsWorkflowRunUrl(variantAnalysisHistoryItem);
const variantAnalysis = variantAnalysisHistoryItem.variantAnalysis;
const fullName = variantAnalysis.controllerRepo.fullName;
expect(actionsWorkflowRunUrl).to.equal(
`https://github.com/${fullName}/actions/runs/${variantAnalysis.actionsWorkflowRunId}`
);
});
});
});

View File

@@ -1,5 +1,5 @@
import { expect } from 'chai';
import { VariantAnalysis, parseVariantAnalysisQueryLanguage, VariantAnalysisQueryLanguage, VariantAnalysisStatus, isVariantAnalysisComplete, VariantAnalysisRepoStatus } from '../../src/remote-queries/shared/variant-analysis';
import { VariantAnalysis, parseVariantAnalysisQueryLanguage, VariantAnalysisQueryLanguage, VariantAnalysisStatus, isVariantAnalysisComplete, VariantAnalysisRepoStatus, getActionsWorkflowRunUrl } from '../../src/remote-queries/shared/variant-analysis';
import { createMockScannedRepo } from '../../src/vscode-tests/factories/remote-queries/shared/scanned-repositories';
import { createMockVariantAnalysis } from '../../src/vscode-tests/factories/remote-queries/shared/variant-analysis';
@@ -101,3 +101,13 @@ describe('isVariantAnalysisComplete', async () => {
});
}
});
describe('getActionsWorkflowRunUrl', () => {
it('should get the run url', () => {
const variantAnalysis = createMockVariantAnalysis();
const actionsWorkflowRunUrl = getActionsWorkflowRunUrl(variantAnalysis);
expect(actionsWorkflowRunUrl).to.equal(`https://github.com/${variantAnalysis.controllerRepo.fullName}/actions/runs/${variantAnalysis.actionsWorkflowRunId}`);
});
});