Commit 4e6f9bee authored by Tomas Vik (OOO back on 2026-08-31)'s avatar Tomas Vik (OOO back on 2026-08-31) 🌴
Browse files

feat: use permalinks in Copy Link to Active File command

parent 5995e4ee
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -65,6 +65,9 @@ module.exports = {
  },
  env: {
    uriScheme: 'vscode',
    clipboard: {
      writeText: jest.fn(),
    },
  },
  CommentMode: { Editing: 0, Preview: 1 },
  StatusBarAlignment: { Left: 0 },
+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();
  });
});
+15 −3
Original line number Diff line number Diff line
@@ -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
@@ -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) {
+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 {}
+7 −0
Original line number Diff line number Diff line
@@ -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');

@@ -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