Commit 7c8b6002 authored by Stanislav Lashmanov's avatar Stanislav Lashmanov
Browse files

fix: concise status bar indicators

parent 3fc79573
Loading
Loading
Loading
Loading
+14 −18
Original line number Diff line number Diff line
@@ -69,7 +69,7 @@ describe('status_bar', () => {
      await statusBar.refresh(createBranchInfo({ pipeline }));
      expect(getPipelineItem().show).toHaveBeenCalled();
      expect(getPipelineItem().hide).not.toHaveBeenCalled();
      expect(getPipelineItem().text).toBe('$(check) GitLab: Pipeline passed');
      expect(getPipelineItem().text).toBe('$(check) Pipeline passed');
    });

    it('prints jobs for running pipeline', async () => {
@@ -93,9 +93,7 @@ describe('status_bar', () => {
      await statusBar.refresh(
        createBranchInfo({ pipeline: { ...pipeline, status: 'running' }, jobs }),
      );
      expect(getPipelineItem().text).toBe(
        '$(pulse) GitLab: Pipeline running (Unit Tests, Integration Tests)',
      );
      expect(getPipelineItem().text).toBe('$(pulse) Pipeline running');
    });

    it('sorts by created time (starts with newer) and deduplicates jobs for running pipeline', async () => {
@@ -122,24 +120,22 @@ describe('status_bar', () => {
      await statusBar.refresh(
        createBranchInfo({ pipeline: { ...pipeline, status: 'running' }, jobs }),
      );
      expect(getPipelineItem().text).toBe(
        '$(pulse) GitLab: Pipeline running (Unit Tests, Integration Tests)',
      );
      expect(getPipelineItem().text).toBe('$(pulse) Pipeline running');
    });

    it('shows no pipeline text when there is no pipeline', async () => {
      await statusBar.refresh(createBranchInfo());
      expect(getPipelineItem().text).toBe('GitLab: No pipeline.');
      expect(getPipelineItem().text).toBe('No pipeline');
    });

    it.each`
      status        | itemText
      ${'running'}  | ${'$(pulse) GitLab: Pipeline running'}
      ${'success'}  | ${'$(check) GitLab: Pipeline passed'}
      ${'pending'}  | ${'$(clock) GitLab: Pipeline pending'}
      ${'failed'}   | ${'$(x) GitLab: Pipeline failed'}
      ${'canceled'} | ${'$(circle-slash) GitLab: Pipeline canceled'}
      ${'skipped'}  | ${'$(diff-renamed) GitLab: Pipeline skipped'}
      ${'running'}  | ${'$(pulse) Pipeline running'}
      ${'success'}  | ${'$(check) Pipeline passed'}
      ${'pending'}  | ${'$(clock) Pipeline pending'}
      ${'failed'}   | ${'$(x) Pipeline failed'}
      ${'canceled'} | ${'$(circle-slash) Pipeline canceled'}
      ${'skipped'}  | ${'$(diff-renamed) Pipeline skipped'}
    `('shows $itemText for pipeline with status $status', async ({ status, itemText }) => {
      await statusBar.refresh(createBranchInfo({ pipeline: { ...pipeline, status } }));
      expect(getPipelineItem().text).toBe(itemText);
@@ -151,7 +147,7 @@ describe('status_bar', () => {
      await statusBar.refresh(createBranchInfo({ mr }));
      expect(getMrItem().show).toHaveBeenCalled();
      expect(getMrItem().hide).not.toHaveBeenCalled();
      expect(getMrItem().text).toBe('$(git-pull-request) GitLab: MR !2000');
      expect(getMrItem().text).toBe('$(git-pull-request) !2000');
      const command = getMrItem().command as vscode.Command;
      expect(command.command).toBe('gl.showRichContent');
      expect(command.arguments?.[0]).toEqual(mr);
@@ -159,7 +155,7 @@ describe('status_bar', () => {

    it('shows create MR text when there is no MR', async () => {
      await statusBar.refresh(createBranchInfo());
      expect(getMrItem().text).toBe('$(git-pull-request) GitLab: Create MR.');
      expect(getMrItem().text).toBe('$(git-pull-request) Create MR');
      expect(getMrItem().command).toBe(USER_COMMANDS.OPEN_CREATE_NEW_MR);
    });
  });
@@ -169,7 +165,7 @@ describe('status_bar', () => {
      await statusBar.refresh(createBranchInfo({ mr, issues: [issue] }));
      expect(getClosingIssueItem().show).toHaveBeenCalled();
      expect(getClosingIssueItem().hide).not.toHaveBeenCalled();
      expect(getClosingIssueItem().text).toBe('$(code) GitLab: Issue #1000');
      expect(getClosingIssueItem().text).toBe('$(code) #1000');
      const command = getClosingIssueItem().command as vscode.Command;
      expect(command.command).toBe('gl.showRichContent');
      expect(command.arguments?.[0]).toEqual(issue);
@@ -177,7 +173,7 @@ describe('status_bar', () => {

    it('shows no issue when there is not a closing issue', async () => {
      await statusBar.refresh(createBranchInfo({ mr, issues: [] }));
      expect(getClosingIssueItem().text).toBe('$(code) GitLab: No issue.');
      expect(getClosingIssueItem().text).toBe('$(code) No issue');
      expect(getClosingIssueItem().command).toBe(undefined);
    });

+11 −51
Original line number Diff line number Diff line
import * as vscode from 'vscode';
import assert = require('assert');
import * as openers from './commands/openers';
import { UserFriendlyError } from './errors/user_friendly_error';
import { log } from './log';
import { PROGRAMMATIC_COMMANDS, USER_COMMANDS } from './command_names';
import { currentBranchRefresher, TreeState } from './current_branch_refresher';
import { ProjectInRepository } from './gitlab/new_project';
import { createStatusBarItem } from './utils/create_status_bar_item';

const MAXIMUM_DISPLAYED_JOBS = 4;

// FIXME: if you are touching this configuration statement, move the configuration to extension_configuration.ts
const {
  showStatusBarLinks,
@@ -29,39 +25,12 @@ const iconForStatus: Record<string, { icon: string; text?: string } | undefined>

const getStatusText = (status: string) => iconForStatus[status]?.text || status;

const createStatusTextFromJobs = (jobs: RestJob[], status: string) => {
  let statusText = getStatusText(status);
  const jobNames = jobs.filter(job => job.status === status).map(job => job.name);
  if (jobNames.length > MAXIMUM_DISPLAYED_JOBS) {
    statusText += ' (';
    statusText += jobNames.slice(0, MAXIMUM_DISPLAYED_JOBS).join(', ');
    statusText += `, +${jobNames.length - MAXIMUM_DISPLAYED_JOBS} jobs`;
    statusText += ')';
  } else if (jobNames.length > 0) {
    statusText += ` (${jobNames.join(', ')})`;
  }
  return statusText;
};

const openIssuableInWebview = (issuable: RestIssuable, rootFsPath: string): vscode.Command => ({
  title: '',
  command: PROGRAMMATIC_COMMANDS.SHOW_RICH_CONTENT,
  arguments: [issuable, rootFsPath],
});

const sortAndDeduplicate = (jobs: RestJob[]): RestJob[] => {
  const alreadyProcessedJob = new Set();
  const compareTimeNewFirst = (a: RestJob, b: RestJob) =>
    Date.parse(a.created_at) < Date.parse(b.created_at) ? -1 : 1;
  return jobs.sort(compareTimeNewFirst).filter(job => {
    if (alreadyProcessedJob.has(job.name)) {
      return false;
    }
    alreadyProcessedJob.add(job.name);
    return true;
  });
};

export class StatusBar {
  pipelineStatusBarItem?: vscode.StatusBarItem;

@@ -101,31 +70,22 @@ export class StatusBar {
  ): Promise<void> {
    if (!this.pipelineStatusBarItem) return;
    if (!pipeline) {
      this.pipelineStatusBarItem.text = 'GitLab: No pipeline.';
      this.pipelineStatusBarItem.text = 'No pipeline';
      this.pipelineStatusBarItem.show();
      this.firstRun = false;
      return;
    }
    const { status } = pipeline;
    let statusText = getStatusText(status);

    if (status === 'running' || status === 'failed') {
      try {
        const processedJobs = sortAndDeduplicate(jobs);
        statusText = createStatusTextFromJobs(processedJobs, status);
      } catch (e) {
        log.error(new UserFriendlyError('Failed to fetch jobs for pipeline.', e));
      }
    }
    const statusText = getStatusText(status);

    const msg = `$(${iconForStatus[status]?.icon}) GitLab: Pipeline ${statusText}`;
    const msg = `$(${iconForStatus[status]?.icon}) Pipeline ${statusText}`;

    if (
      showPipelineUpdateNotifications &&
      this.pipelineStatusBarItem.text !== msg &&
      !this.firstRun
    ) {
      const message = `Pipeline ${statusText}.`;
      const message = `Pipeline ${statusText}`;

      await vscode.window
        .showInformationMessage(message, { modal: false }, 'View in Gitlab')
@@ -148,12 +108,12 @@ export class StatusBar {
  ): void {
    if (!this.mrIssueStatusBarItem) return;
    if (mr) {
      let text = `$(code) GitLab: No issue.`;
      let text = `$(code) No issue`;
      let command;

      const firstIssue = closingIssues[0];
      if (firstIssue) {
        text = `$(code) GitLab: Issue #${firstIssue.iid}`;
        text = `$(code) #${firstIssue.iid}`;
        command = openIssuableInWebview(firstIssue, rootFsPath);
      }

@@ -172,8 +132,8 @@ export class StatusBar {
      ? openIssuableInWebview(mr, rootFsPath)
      : USER_COMMANDS.OPEN_CREATE_NEW_MR;
    this.mrStatusBarItem.text = mr
      ? `$(git-pull-request) GitLab: MR !${mr.iid}`
      : '$(git-pull-request) GitLab: Create MR.';
      ? `$(git-pull-request) !${mr.iid}`
      : '$(git-pull-request) Create MR';
  }

  init(): void {
@@ -184,7 +144,7 @@ export class StatusBar {
        priority: 2,
        id: 'gl.status.pipeline',
        name: 'GitLab Workflow: Pipeline',
        initialText: '$(info) GitLab: Fetching pipeline...',
        initialText: '$(info) Fetching pipeline...',
        command: USER_COMMANDS.PIPELINE_ACTIONS,
      });
      if (showMrStatusOnStatusBar) {
@@ -192,14 +152,14 @@ export class StatusBar {
          priority: 1,
          id: 'gl.status.mr',
          name: 'GitLab Workflow: Merge Request',
          initialText: '$(info) GitLab: Finding MR...',
          initialText: '$(info) Finding MR...',
        });
        if (showIssueLinkOnStatusBar) {
          this.mrIssueStatusBarItem = createStatusBarItem({
            priority: 0,
            id: 'gl.status.issue',
            name: 'GitLab Workflow: Issue',
            initialText: '$(info) GitLab: Fetching closing issue...',
            initialText: '$(info) Fetching closing issue...',
          });
        }
      }
+1 −1
Original line number Diff line number Diff line
@@ -52,7 +52,7 @@ describe('GitLab status bar', () => {

    assert.strictEqual(vscode.window.createStatusBarItem.firstCall.firstArg, 'gl.status.pipeline');
    const pipelineItem = statusBar.pipelineStatusBarItem;
    assert.strictEqual(pipelineItem.text, '$(check) GitLab: Pipeline passed');
    assert.strictEqual(pipelineItem.text, '$(check) Pipeline passed');
    assert.strictEqual(pipelineItem.show.called, true);
    assert.strictEqual(pipelineItem.hide.called, false);
    assert.strictEqual(pipelineItem.command, USER_COMMANDS.PIPELINE_ACTIONS);