Commit 8e300e54 authored by Lennard Sprong's avatar Lennard Sprong
Browse files

feat: Don't use Diff when comparing empty media file

parent 8cb6c732
Loading
Loading
Loading
Loading
+20 −2
Original line number Diff line number Diff line
import * as vscode from 'vscode';
import { changeTypeDecorationProvider, decorations } from './change_type_decoration_provider';
import { ADDED, DELETED, RENAMED, MODIFIED, CHANGE_TYPE_QUERY_KEY } from '../constants';
import { toReviewUri } from './review_uri';

describe('FileDecoratorProvider', () => {
  it.each`
  const commonParams = {
    path: './test',
    exists: true,
    mrId: 1,
    projectId: 1,
    repositoryRoot: '/',
  };

  describe.each`
    changeType  | decoration
    ${ADDED}    | ${decorations[ADDED]}
    ${DELETED}  | ${decorations[DELETED]}
    ${RENAMED}  | ${decorations[RENAMED]}
    ${MODIFIED} | ${decorations[MODIFIED]}
  `('Correctly maps changeType to decorator', ({ changeType, decoration }) => {
  `('Change type $changeType', ({ changeType, decoration }) => {
    it('decorates File urls', () => {
      const uri: vscode.Uri = vscode.Uri.file(`./test?${CHANGE_TYPE_QUERY_KEY}=${changeType}`);
      const { token } = new vscode.CancellationTokenSource();
      const returnValue = changeTypeDecorationProvider.provideFileDecoration(uri, token);

      expect(returnValue).toEqual(decoration);
    });
    it('decorates Review urls', () => {
      const uri: vscode.Uri = toReviewUri({ ...commonParams, change: changeType });
      const { token } = new vscode.CancellationTokenSource();
      const returnValue = changeTypeDecorationProvider.provideFileDecoration(uri, token);

      expect(returnValue).toEqual(decoration);
    });
  });
});
+17 −1
Original line number Diff line number Diff line
import * as vscode from 'vscode';
import { ADDED, DELETED, RENAMED, MODIFIED, CHANGE_TYPE_QUERY_KEY } from '../constants';
import {
  ADDED,
  DELETED,
  RENAMED,
  MODIFIED,
  CHANGE_TYPE_QUERY_KEY,
  REVIEW_URI_SCHEME,
} from '../constants';
import { fromReviewUri } from './review_uri';

export const decorations: Record<string, vscode.FileDecoration | undefined> = {
  [ADDED]: {
@@ -21,6 +29,14 @@ export const decorations: Record<string, vscode.FileDecoration | undefined> = {

export const changeTypeDecorationProvider: vscode.FileDecorationProvider = {
  provideFileDecoration: uri => {
    if (uri.scheme === REVIEW_URI_SCHEME) {
      const params = fromReviewUri(uri);
      const changeType = params.change;
      if (changeType) {
        return decorations[changeType];
      }
      return decorations[DELETED];
    }
    if (uri.scheme === 'file') {
      const params = new URLSearchParams(uri.query);
      const changeType = params.get(CHANGE_TYPE_QUERY_KEY);
+5 −2
Original line number Diff line number Diff line
@@ -5,6 +5,7 @@ import { jsonStringifyWithSortedKeys } from '../utils/json_stringify_with_sorted
export interface ReviewParams {
  path: string;
  exists: boolean;
  change?: string;
  commit?: string;
  // if the review URI only contains the mandatory params, we treat it as an empty file URI (that's used when showing diff of added/deleted file)
  repositoryRoot: string;
@@ -15,12 +16,13 @@ export interface ReviewParams {
export function toReviewUri({
  path,
  exists,
  change,
  commit,
  repositoryRoot,
  projectId,
  mrId,
}: ReviewParams): Uri {
  const query = { commit, exists: exists ? '1' : '', repositoryRoot, projectId, mrId };
  const query = { commit, change, exists: exists ? '1' : '', repositoryRoot, projectId, mrId };
  return Uri.file(path).with({
    scheme: REVIEW_URI_SCHEME,
    query: jsonStringifyWithSortedKeys(query),
@@ -28,11 +30,12 @@ export function toReviewUri({
}

export function fromReviewUri(uri: Uri): ReviewParams {
  const { commit, exists, repositoryRoot, projectId, mrId } = JSON.parse(uri.query);
  const { commit, change, exists, repositoryRoot, projectId, mrId } = JSON.parse(uri.query);
  return {
    path: uri.path,
    exists: !!exists,
    commit,
    change,
    repositoryRoot,
    projectId,
    mrId,
+46 −0
Original line number Diff line number Diff line
import { VS_COMMANDS } from '../../command_names';
import {
  ADDED,
  CHANGE_TYPE_QUERY_KEY,
@@ -12,6 +13,49 @@ import { ChangedFileItem } from './changed_file_item';

describe('ChangedFileItem', () => {
  describe('image file', () => {
    const extensions = ['.jpg', '.png', '.mp3', '.webm'];

    it.each(extensions)('should show diff for changed %s file', extension => {
      const changedImageFile = { ...diffFile, new_path: `file${extension}` };
      const item = new ChangedFileItem(mr, mrVersion, changedImageFile, '/repo', () => false);

      expect(item.command?.command).toBe(VS_COMMANDS.DIFF);
    });

    it.each(extensions)('should not show diff for new %s file', extension => {
      const path = `newfile${extension}`;
      const changedImageFile = {
        ...diffFile,
        renamed_file: false,
        new_file: true,
        old_path: `invalid${extension}`,
        new_path: path,
      };
      const item = new ChangedFileItem(mr, mrVersion, changedImageFile, '/repo', () => false);

      expect(item.command?.command).toBe(VS_COMMANDS.OPEN);

      const [url] = item.command?.arguments ?? [];
      expect(url.path).toBe(path);
    });

    it.each(extensions)('should not show diff for deleted %s file', extension => {
      const path = `deleted${extension}`;
      const changedImageFile = {
        ...diffFile,
        renamed_file: false,
        deleted_file: true,
        old_path: path,
        new_path: `invalid${extension}`,
      };
      const item = new ChangedFileItem(mr, mrVersion, changedImageFile, '/repo', () => false);

      expect(item.command?.command).toBe(VS_COMMANDS.OPEN);

      const [url] = item.command?.arguments ?? [];
      expect(url.path).toBe(path);
    });

    it.each`
      file                                                                          | changeType
      ${{ ...diffFile, new_file: true, deleted_file: false, renamed_file: false }}  | ${ADDED}
@@ -49,6 +93,7 @@ describe('ChangedFileItem', () => {
        toReviewUri({
          path: diffFile.old_path,
          exists: true,
          change: 'renamed',
          commit: mrVersion.base_commit_sha,
          repositoryRoot: '/repository/fsPath',
          projectId: mr.project_id,
@@ -59,6 +104,7 @@ describe('ChangedFileItem', () => {
        toReviewUri({
          path: diffFile.new_path,
          exists: true,
          change: 'renamed',
          commit: mrVersion.head_commit_sha,
          repositoryRoot: '/repository/fsPath',
          projectId: mr.project_id,
+35 −1
Original line number Diff line number Diff line
import * as vscode from 'vscode';
import { posix as path } from 'path';
import { toReviewUri } from '../../review/review_uri';
import { isEmptyFileUri, toReviewUri } from '../../review/review_uri';
import { VS_COMMANDS } from '../../command_names';
import {
  ADDED,
@@ -21,6 +21,28 @@ const getChangeType = (file: RestDiffFile): ChangeType => {
  return MODIFIED;
};

// All extensions supported by VS Code's built-in Media Previewer.
// https://github.com/microsoft/vscode/blob/ba38f8a5eeb81c05dbdade3e1657e233d770f717/extensions/media-preview/package.json
const mediaExtensions = [
  '.jpg',
  '.jpe',
  '.jpeg',
  '.png',
  '.bmp',
  '.gif',
  '.ico',
  '.webp',
  '.avif',
  '.mp3',
  '.wav',
  '.ogg',
  '.oga',
  '.mp4',
  '.webm',
];
const looksLikeMedia = (filePath: string) =>
  mediaExtensions.includes(path.extname(filePath).toLowerCase());

const getBaseAndHeadUri = (
  mr: RestMr,
  mrVersion: RestMrVersion,
@@ -29,6 +51,7 @@ const getBaseAndHeadUri = (
) => {
  const commonParams = {
    repositoryRoot: repositoryPath,
    change: getChangeType(file),
    projectId: mr.project_id,
    mrId: mr.id,
  };
@@ -69,12 +92,22 @@ export class ChangedFileItem extends vscode.TreeItem {
    const uris = getBaseAndHeadUri(mr, mrVersion, file, repositoryPath);
    this.headFileUri = uris.headFileUri;
    this.baseFileUri = uris.baseFileUri;
    const headIsEmpty = isEmptyFileUri(uris.headFileUri);
    const baseIsEmpty = isEmptyFileUri(uris.baseFileUri);
    const hasComments = hasComment(this.baseFileUri) || hasComment(this.headFileUri);
    const query = new URLSearchParams([
      [CHANGE_TYPE_QUERY_KEY, getChangeType(file)],
      [HAS_COMMENTS_QUERY_KEY, String(hasComments)],
    ]).toString();
    this.resourceUri = this.resourceUri?.with({ query });

    if (looksLikeMedia(uris.headFileUri.path) && (headIsEmpty || baseIsEmpty)) {
      this.command = {
        title: 'Show changes',
        command: VS_COMMANDS.OPEN,
        arguments: [headIsEmpty ? this.baseFileUri : this.headFileUri],
      };
    } else {
      this.command = {
        title: 'Show changes',
        command: VS_COMMANDS.DIFF,
@@ -86,3 +119,4 @@ export class ChangedFileItem extends vscode.TreeItem {
      };
    }
  }
}