Loading desktop.package.json +5 −0 Original line number Diff line number Diff line Loading @@ -297,6 +297,11 @@ "enablement": "gitlab.canScrollToBottom", "category": "GitLab", "icon": "$(fold-down)" }, { "command": "gl.viewSecurityFinding", "title": "View Security finding details", "category": "GitLab" } ], "menus": { Loading scripts/utils/desktop_jobs.mjs +8 −0 Original line number Diff line number Diff line Loading @@ -15,6 +15,13 @@ export async function copyPendingJobAssets() { ); } export async function copySecurityFindingAssets() { return copyFile( path.join(root, 'webviews/security_finding.html'), path.join(root, `dist-desktop/webviews/security_finding.html`), ); } export async function compileSource() { await run('tsc', ['-p', root]); } Loading Loading @@ -69,6 +76,7 @@ export async function buildDesktop() { await Promise.all([ prepareWebviews(desktopWebviews, ENVIRONMENTS.DESKTOP), copyPendingJobAssets(), copySecurityFindingAssets(), checkAndBuildExtension(['--minify']), generateAssets(packageJson, ENVIRONMENTS.DESKTOP), ]); Loading scripts/watch_desktop.mjs +7 −1 Original line number Diff line number Diff line import { copyPendingJobAssets, watchDesktop, watchWebviews } from './utils/desktop_jobs.mjs'; import { copyPendingJobAssets, copySecurityFindingAssets, watchDesktop, watchWebviews, } from './utils/desktop_jobs.mjs'; import { commonJobs, generateAssets, writePackageJson } from './utils/common_jobs.mjs'; import { createDesktopPackageJson } from './utils/packages.mjs'; import { ENVIRONMENTS } from './constants.mjs'; Loading @@ -11,6 +16,7 @@ async function main() { writePackageJson(packageJson, ENVIRONMENTS.DESKTOP), generateAssets(packageJson, ENVIRONMENTS.DESKTOP), copyPendingJobAssets(), copySecurityFindingAssets(), ]); const abortController = new AbortController(); Loading src/desktop/ci/security_finding_controller.test.ts 0 → 100644 +37 −0 Original line number Diff line number Diff line import * as vscode from 'vscode'; import { asMock } from '../test_utils/as_mock'; import { createExtensionContext, securityReport } from '../test_utils/entities'; import { SecurityFindingWebviewController } from './security_finding_controller'; const finding = securityReport.fixed[0]; describe('SecurityFindingWebviewController', () => { let controller: SecurityFindingWebviewController; beforeEach(async () => { asMock(vscode.window.createWebviewPanel).mockImplementation(() => ({ webview: {}, onDidDispose: jest.fn(), reveal: jest.fn(), })); controller = new SecurityFindingWebviewController(); await controller.init(createExtensionContext()); }); afterEach(() => { jest.resetAllMocks(); }); it('creates and updates panel correctly', () => { const panel = controller.open(finding); expect(panel.title).toBe(finding.title); const webviewHtml = panel.webview.html; expect(webviewHtml).toContain(finding.title); expect(webviewHtml).toContain(finding.description); expect(webviewHtml).toContain(finding.severity); controller.open({ ...finding, title: 'test' }); expect(panel.title).toBe('test'); }); }); src/desktop/ci/security_finding_controller.ts 0 → 100644 +72 −0 Original line number Diff line number Diff line import { promises as fs } from 'fs'; import * as path from 'path'; import * as vscode from 'vscode'; import assert from 'assert'; import { WEBVIEW_SECURITY_FINDING } from '../constants'; import { GqlSecurityFinding } from '../gitlab/security_findings/api/get_security_finding_report'; export type SecurityFindingWebviewPanel = vscode.WebviewPanel & { finding: GqlSecurityFinding; }; export class SecurityFindingWebviewController { async init(context: vscode.ExtensionContext) { this.#context = context; this.#htmlContent = await fs.readFile( path.join(this.#context.extensionPath, 'webviews/security_finding.html'), 'utf-8', ); } #context?: vscode.ExtensionContext; #panel?: SecurityFindingWebviewPanel; #htmlContent = ''; #createEmptyPanel(): SecurityFindingWebviewPanel { const panel = vscode.window.createWebviewPanel( WEBVIEW_SECURITY_FINDING, '', vscode.ViewColumn.Active, {}, ) as SecurityFindingWebviewPanel; this.#panel = panel; panel.onDidDispose(() => { if (this.#panel === panel) { this.#panel = undefined; } }); return panel; } createOrUpdateWebview( finding: GqlSecurityFinding, existingPanel?: SecurityFindingWebviewPanel, ): SecurityFindingWebviewPanel { assert(this.#context); const { title, description, severity } = finding; const panel = existingPanel ?? this.#createEmptyPanel(); panel.title = title; panel.finding = finding; panel.webview.html = this.#htmlContent .replace('{{title}}', title) .replace('{{description}}', description) .replace('{{severity}}', severity); return panel; } open(item: GqlSecurityFinding): SecurityFindingWebviewPanel { const panel = this.createOrUpdateWebview(item, this.#panel); panel.reveal(); return panel; } } export const securityFindingWebviewController = new SecurityFindingWebviewController(); Loading
desktop.package.json +5 −0 Original line number Diff line number Diff line Loading @@ -297,6 +297,11 @@ "enablement": "gitlab.canScrollToBottom", "category": "GitLab", "icon": "$(fold-down)" }, { "command": "gl.viewSecurityFinding", "title": "View Security finding details", "category": "GitLab" } ], "menus": { Loading
scripts/utils/desktop_jobs.mjs +8 −0 Original line number Diff line number Diff line Loading @@ -15,6 +15,13 @@ export async function copyPendingJobAssets() { ); } export async function copySecurityFindingAssets() { return copyFile( path.join(root, 'webviews/security_finding.html'), path.join(root, `dist-desktop/webviews/security_finding.html`), ); } export async function compileSource() { await run('tsc', ['-p', root]); } Loading Loading @@ -69,6 +76,7 @@ export async function buildDesktop() { await Promise.all([ prepareWebviews(desktopWebviews, ENVIRONMENTS.DESKTOP), copyPendingJobAssets(), copySecurityFindingAssets(), checkAndBuildExtension(['--minify']), generateAssets(packageJson, ENVIRONMENTS.DESKTOP), ]); Loading
scripts/watch_desktop.mjs +7 −1 Original line number Diff line number Diff line import { copyPendingJobAssets, watchDesktop, watchWebviews } from './utils/desktop_jobs.mjs'; import { copyPendingJobAssets, copySecurityFindingAssets, watchDesktop, watchWebviews, } from './utils/desktop_jobs.mjs'; import { commonJobs, generateAssets, writePackageJson } from './utils/common_jobs.mjs'; import { createDesktopPackageJson } from './utils/packages.mjs'; import { ENVIRONMENTS } from './constants.mjs'; Loading @@ -11,6 +16,7 @@ async function main() { writePackageJson(packageJson, ENVIRONMENTS.DESKTOP), generateAssets(packageJson, ENVIRONMENTS.DESKTOP), copyPendingJobAssets(), copySecurityFindingAssets(), ]); const abortController = new AbortController(); Loading
src/desktop/ci/security_finding_controller.test.ts 0 → 100644 +37 −0 Original line number Diff line number Diff line import * as vscode from 'vscode'; import { asMock } from '../test_utils/as_mock'; import { createExtensionContext, securityReport } from '../test_utils/entities'; import { SecurityFindingWebviewController } from './security_finding_controller'; const finding = securityReport.fixed[0]; describe('SecurityFindingWebviewController', () => { let controller: SecurityFindingWebviewController; beforeEach(async () => { asMock(vscode.window.createWebviewPanel).mockImplementation(() => ({ webview: {}, onDidDispose: jest.fn(), reveal: jest.fn(), })); controller = new SecurityFindingWebviewController(); await controller.init(createExtensionContext()); }); afterEach(() => { jest.resetAllMocks(); }); it('creates and updates panel correctly', () => { const panel = controller.open(finding); expect(panel.title).toBe(finding.title); const webviewHtml = panel.webview.html; expect(webviewHtml).toContain(finding.title); expect(webviewHtml).toContain(finding.description); expect(webviewHtml).toContain(finding.severity); controller.open({ ...finding, title: 'test' }); expect(panel.title).toBe('test'); }); });
src/desktop/ci/security_finding_controller.ts 0 → 100644 +72 −0 Original line number Diff line number Diff line import { promises as fs } from 'fs'; import * as path from 'path'; import * as vscode from 'vscode'; import assert from 'assert'; import { WEBVIEW_SECURITY_FINDING } from '../constants'; import { GqlSecurityFinding } from '../gitlab/security_findings/api/get_security_finding_report'; export type SecurityFindingWebviewPanel = vscode.WebviewPanel & { finding: GqlSecurityFinding; }; export class SecurityFindingWebviewController { async init(context: vscode.ExtensionContext) { this.#context = context; this.#htmlContent = await fs.readFile( path.join(this.#context.extensionPath, 'webviews/security_finding.html'), 'utf-8', ); } #context?: vscode.ExtensionContext; #panel?: SecurityFindingWebviewPanel; #htmlContent = ''; #createEmptyPanel(): SecurityFindingWebviewPanel { const panel = vscode.window.createWebviewPanel( WEBVIEW_SECURITY_FINDING, '', vscode.ViewColumn.Active, {}, ) as SecurityFindingWebviewPanel; this.#panel = panel; panel.onDidDispose(() => { if (this.#panel === panel) { this.#panel = undefined; } }); return panel; } createOrUpdateWebview( finding: GqlSecurityFinding, existingPanel?: SecurityFindingWebviewPanel, ): SecurityFindingWebviewPanel { assert(this.#context); const { title, description, severity } = finding; const panel = existingPanel ?? this.#createEmptyPanel(); panel.title = title; panel.finding = finding; panel.webview.html = this.#htmlContent .replace('{{title}}', title) .replace('{{description}}', description) .replace('{{severity}}', severity); return panel; } open(item: GqlSecurityFinding): SecurityFindingWebviewPanel { const panel = this.createOrUpdateWebview(item, this.#panel); panel.reveal(); return panel; } } export const securityFindingWebviewController = new SecurityFindingWebviewController();