Loading src/__mocks__/vscode.js +3 −0 Original line number Diff line number Diff line Loading @@ -65,6 +65,9 @@ module.exports = { }, env: { uriScheme: 'vscode', clipboard: { writeText: jest.fn(), }, }, CommentMode: { Editing: 0, Preview: 1 }, StatusBarAlignment: { Left: 0 }, Loading src/commands/openers.test.ts 0 → 100644 +61 −0 Original line number Diff line number Diff line import * as vscode from 'vscode'; import path from 'path'; import { GitRepository } from '../git/new_git'; import { ProjectInRepository } from '../gitlab/new_project'; import * as testEntities from '../test_utils/entities'; import { createFakeRepository } from '../test_utils/fake_git_extension'; import { copyLinkToActiveFile } from './openers'; import { Repository } from '../api/git'; import { WarningError } from '../errors/warning_error'; const TEST_HASH = 'abcdefg'; describe('openers', () => { let rawRepository: Repository; let activeEditor: vscode.TextEditor; let pir: ProjectInRepository; beforeEach(() => { const repoRootPath = path.join('path', 'to', 'repo'); rawRepository = createFakeRepository(); pir = { ...testEntities.projectInRepository, pointer: { ...testEntities.projectInRepository.pointer, repository: { rootFsPath: repoRootPath, rawRepository, } as GitRepository, }, }; activeEditor = { document: { uri: vscode.Uri.file(path.join(repoRootPath, 'file')) }, selection: { start: { line: 1 }, end: { line: 2 }, }, } as unknown as vscode.TextEditor; }); it('copyLinkToActiveFile creates permalink when active file is versioned', async () => { rawRepository.log = async () => [{ hash: TEST_HASH, message: '', parents: [] }]; await copyLinkToActiveFile({ projectInRepository: pir, activeEditor }); expect(vscode.env.clipboard.writeText).toHaveBeenCalledWith( `https://gitlab.com/gitlab-org/gitlab-vscode-extension/blob/${TEST_HASH}/file#L2-3`, ); }); it('copyLinkToActiveFile shows error when active file is not versioned', async () => { rawRepository.log = async () => []; const copyResult = copyLinkToActiveFile({ projectInRepository: pir, activeEditor }); await expect(copyResult).rejects.toThrow(WarningError); await expect(copyResult).rejects.toThrow(/No link exists for the current file/); expect(vscode.env.clipboard.writeText).not.toHaveBeenCalled(); }); }); src/commands/openers.ts +15 −3 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ import { DetachedHeadError } from '../errors/detached_head_error'; import { Repository } from '../api/git'; import { JobItemModel } from '../tree_view/items/job_item_model'; import { PipelineItemModel } from '../tree_view/items/pipeline_item_model'; import { WarningError } from '../errors/warning_error'; export const openUrl = async (url: string): Promise<void> => { // workaround for a VS Code open command bug: https://gitlab.com/gitlab-org/gitlab-vscode-extension/-/issues/44 Loading Loading @@ -66,14 +67,25 @@ export const showMergeRequests: ProjectCommand = async projectInRepository => { await openTemplatedLink('$projectUrl/merge_requests?assignee_id=$userId', projectInRepository); }; async function getActiveFile({ projectInRepository, activeEditor }: ProjectInRepositoryAndFile) { async function getActiveFile({ projectInRepository, activeEditor, }: ProjectInRepositoryAndFile): Promise<string> { const { repository } = projectInRepository.pointer; const branchName = await getTrackingBranchNameOrThrow(repository.rawRepository); const filePath = path .relative(repository.rootFsPath, activeEditor.document.uri.fsPath) .replace(/\\/g, '/'); const log = await repository.rawRepository.log({ maxEntries: 1, path: filePath }); if (log.length === 0) { throw new WarningError( 'No link exists for the current file. Commit the current file to the repository.', ); } const { project } = projectInRepository; const fileUrl = `${project.webUrl}/blob/${encodeURIComponent(branchName)}/${filePath}`; const fileUrl = `${project.webUrl}/blob/${encodeURIComponent(log[0].hash)}/${filePath}`; let anchor = ''; if (activeEditor.selection) { Loading src/errors/warning_error.ts 0 → 100644 +6 −0 Original line number Diff line number Diff line /** When this error bubbles up to the `handleError` logic, * we show it to the user as a warning. * * Create it the same way as the plain error (`new WarningError(message)`) */ export class WarningError extends Error {} src/log.test.ts +7 −0 Original line number Diff line number Diff line Loading @@ -4,6 +4,7 @@ import { handleError, initializeLogging, log } from './log'; import { USER_COMMANDS } from './command_names'; import { asMock } from './test_utils/as_mock'; import { getExtensionConfiguration } from './utils/extension_configuration'; import { WarningError } from './errors/warning_error'; jest.mock('./utils/extension_configuration'); Loading Loading @@ -112,5 +113,11 @@ describe('logging', () => { expect(executeCommand).toBeCalledWith(USER_COMMANDS.SHOW_OUTPUT); }); it('shows WarningError as warning', async () => { await handleError(new WarningError(message)).onlyForTesting; expect(vscode.window.showWarningMessage).toBeCalledWith(message); }); }); }); Loading
src/__mocks__/vscode.js +3 −0 Original line number Diff line number Diff line Loading @@ -65,6 +65,9 @@ module.exports = { }, env: { uriScheme: 'vscode', clipboard: { writeText: jest.fn(), }, }, CommentMode: { Editing: 0, Preview: 1 }, StatusBarAlignment: { Left: 0 }, Loading
src/commands/openers.test.ts 0 → 100644 +61 −0 Original line number Diff line number Diff line import * as vscode from 'vscode'; import path from 'path'; import { GitRepository } from '../git/new_git'; import { ProjectInRepository } from '../gitlab/new_project'; import * as testEntities from '../test_utils/entities'; import { createFakeRepository } from '../test_utils/fake_git_extension'; import { copyLinkToActiveFile } from './openers'; import { Repository } from '../api/git'; import { WarningError } from '../errors/warning_error'; const TEST_HASH = 'abcdefg'; describe('openers', () => { let rawRepository: Repository; let activeEditor: vscode.TextEditor; let pir: ProjectInRepository; beforeEach(() => { const repoRootPath = path.join('path', 'to', 'repo'); rawRepository = createFakeRepository(); pir = { ...testEntities.projectInRepository, pointer: { ...testEntities.projectInRepository.pointer, repository: { rootFsPath: repoRootPath, rawRepository, } as GitRepository, }, }; activeEditor = { document: { uri: vscode.Uri.file(path.join(repoRootPath, 'file')) }, selection: { start: { line: 1 }, end: { line: 2 }, }, } as unknown as vscode.TextEditor; }); it('copyLinkToActiveFile creates permalink when active file is versioned', async () => { rawRepository.log = async () => [{ hash: TEST_HASH, message: '', parents: [] }]; await copyLinkToActiveFile({ projectInRepository: pir, activeEditor }); expect(vscode.env.clipboard.writeText).toHaveBeenCalledWith( `https://gitlab.com/gitlab-org/gitlab-vscode-extension/blob/${TEST_HASH}/file#L2-3`, ); }); it('copyLinkToActiveFile shows error when active file is not versioned', async () => { rawRepository.log = async () => []; const copyResult = copyLinkToActiveFile({ projectInRepository: pir, activeEditor }); await expect(copyResult).rejects.toThrow(WarningError); await expect(copyResult).rejects.toThrow(/No link exists for the current file/); expect(vscode.env.clipboard.writeText).not.toHaveBeenCalled(); }); });
src/commands/openers.ts +15 −3 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ import { DetachedHeadError } from '../errors/detached_head_error'; import { Repository } from '../api/git'; import { JobItemModel } from '../tree_view/items/job_item_model'; import { PipelineItemModel } from '../tree_view/items/pipeline_item_model'; import { WarningError } from '../errors/warning_error'; export const openUrl = async (url: string): Promise<void> => { // workaround for a VS Code open command bug: https://gitlab.com/gitlab-org/gitlab-vscode-extension/-/issues/44 Loading Loading @@ -66,14 +67,25 @@ export const showMergeRequests: ProjectCommand = async projectInRepository => { await openTemplatedLink('$projectUrl/merge_requests?assignee_id=$userId', projectInRepository); }; async function getActiveFile({ projectInRepository, activeEditor }: ProjectInRepositoryAndFile) { async function getActiveFile({ projectInRepository, activeEditor, }: ProjectInRepositoryAndFile): Promise<string> { const { repository } = projectInRepository.pointer; const branchName = await getTrackingBranchNameOrThrow(repository.rawRepository); const filePath = path .relative(repository.rootFsPath, activeEditor.document.uri.fsPath) .replace(/\\/g, '/'); const log = await repository.rawRepository.log({ maxEntries: 1, path: filePath }); if (log.length === 0) { throw new WarningError( 'No link exists for the current file. Commit the current file to the repository.', ); } const { project } = projectInRepository; const fileUrl = `${project.webUrl}/blob/${encodeURIComponent(branchName)}/${filePath}`; const fileUrl = `${project.webUrl}/blob/${encodeURIComponent(log[0].hash)}/${filePath}`; let anchor = ''; if (activeEditor.selection) { Loading
src/errors/warning_error.ts 0 → 100644 +6 −0 Original line number Diff line number Diff line /** When this error bubbles up to the `handleError` logic, * we show it to the user as a warning. * * Create it the same way as the plain error (`new WarningError(message)`) */ export class WarningError extends Error {}
src/log.test.ts +7 −0 Original line number Diff line number Diff line Loading @@ -4,6 +4,7 @@ import { handleError, initializeLogging, log } from './log'; import { USER_COMMANDS } from './command_names'; import { asMock } from './test_utils/as_mock'; import { getExtensionConfiguration } from './utils/extension_configuration'; import { WarningError } from './errors/warning_error'; jest.mock('./utils/extension_configuration'); Loading Loading @@ -112,5 +113,11 @@ describe('logging', () => { expect(executeCommand).toBeCalledWith(USER_COMMANDS.SHOW_OUTPUT); }); it('shows WarningError as warning', async () => { await handleError(new WarningError(message)).onlyForTesting; expect(vscode.window.showWarningMessage).toBeCalledWith(message); }); }); });