Commit d7b4ea01 authored by Dylan Bernardi's avatar Dylan Bernardi Committed by Paul Slaughter
Browse files

fix: FeedbackForm snowplow event missing "environment"

parent f15452e8
Loading
Loading
Loading
Loading
+69 −1
Original line number Diff line number Diff line
import { GitLabPlatformForAccount, GitLabPlatformManager } from '../platform/gitlab_platform';
import { GitLabPlatformManagerForChat } from './get_platform_manager_for_chat';
import {
  GitLabEnvironment,
  GitLabPlatformManagerForChat,
  GITLAB_COM_URL,
  GITLAB_ORG_URL,
  GITLAB_DEVELOPMENT_URL,
  GITLAB_STAGING_URL,
} from './get_platform_manager_for_chat';
import { account, gitlabPlatformForAccount } from '../test_utils/entities';
import { Account } from '../platform/gitlab_account';
import { createFakePartial } from '../test_utils/create_fake_partial';
@@ -116,4 +123,65 @@ describe('GitLabPlatformManagerForChat', () => {
      expect(await platformManagerForChat.getGitLabPlatform()).toBe(undefined);
    });
  });

  describe('when getGitLabEnvironment should return the correct instance', () => {
    let customGitlabPlatformForAccount: GitLabPlatformForAccount;
    beforeEach(() => {
      customGitlabPlatformForAccount = firstGitlabPlatformForAccount;

      jest
        .mocked(gitlabPlatformManager.getForAllAccounts)
        .mockResolvedValueOnce([firstGitlabPlatformForAccount]);
    });

    it('should returns GITLAB_COM for GITLAB_COM_URL instance', async () => {
      customGitlabPlatformForAccount.account.instanceUrl = GITLAB_COM_URL;
      jest
        .mocked(getChatSupport)
        .mockResolvedValue({ hasSupportForChat: true, platform: customGitlabPlatformForAccount });
      expect(await platformManagerForChat.getGitLabEnvironment()).toBe(
        GitLabEnvironment.GITLAB_COM,
      );
    });

    it('should returns GITLAB_ORG for GITLAB_COM_URL instance', async () => {
      customGitlabPlatformForAccount.account.instanceUrl = GITLAB_ORG_URL;
      jest
        .mocked(getChatSupport)
        .mockResolvedValue({ hasSupportForChat: true, platform: customGitlabPlatformForAccount });
      expect(await platformManagerForChat.getGitLabEnvironment()).toBe(
        GitLabEnvironment.GITLAB_ORG,
      );
    });

    it('should returns GITLAB_DEVELOPMENT for GITLAB_DEVELOPMENT_URL instance', async () => {
      customGitlabPlatformForAccount.account.instanceUrl = GITLAB_DEVELOPMENT_URL;
      jest
        .mocked(getChatSupport)
        .mockResolvedValue({ hasSupportForChat: true, platform: customGitlabPlatformForAccount });
      expect(await platformManagerForChat.getGitLabEnvironment()).toBe(
        GitLabEnvironment.GITLAB_DEVELOPMENT,
      );
    });

    it('should returns GITLAB_STAGING for GITLAB_STAGING_URL instance', async () => {
      customGitlabPlatformForAccount.account.instanceUrl = GITLAB_STAGING_URL;
      jest
        .mocked(getChatSupport)
        .mockResolvedValue({ hasSupportForChat: true, platform: customGitlabPlatformForAccount });
      expect(await platformManagerForChat.getGitLabEnvironment()).toBe(
        GitLabEnvironment.GITLAB_STAGING,
      );
    });

    it('should returns GITLAB_STAGING_URL for any other instanceUrl', async () => {
      customGitlabPlatformForAccount.account.instanceUrl = '';
      jest
        .mocked(getChatSupport)
        .mockResolvedValue({ hasSupportForChat: true, platform: customGitlabPlatformForAccount });
      expect(await platformManagerForChat.getGitLabEnvironment()).toBe(
        GitLabEnvironment.GITLAB_SELF_MANAGED,
      );
    });
  });
});
+31 −0
Original line number Diff line number Diff line
import { GitLabPlatformManager, GitLabPlatformForAccount } from '../platform/gitlab_platform';
import { getChatSupport } from './api/get_chat_support';

export const GITLAB_COM_URL: string = 'https://gitlab.com';
export const GITLAB_STAGING_URL: string = 'https://staging.gitlab.com';
export const GITLAB_ORG_URL: string = 'https://dev.gitlab.org';
export const GITLAB_DEVELOPMENT_URL: string = 'http://localhost';

export enum GitLabEnvironment {
  GITLAB_COM = 'production',
  GITLAB_STAGING = 'staging',
  GITLAB_ORG = 'org',
  GITLAB_DEVELOPMENT = 'development',
  GITLAB_SELF_MANAGED = 'self-managed',
}

export class GitLabPlatformManagerForChat {
  readonly #platformManager: GitLabPlatformManager;

@@ -35,4 +48,22 @@ export class GitLabPlatformManagerForChat {

    return platform;
  }

  async getGitLabEnvironment(): Promise<GitLabEnvironment> {
    const platform = await this.getGitLabPlatform();
    const instanceUrl = platform?.account.instanceUrl;

    switch (instanceUrl) {
      case GITLAB_COM_URL:
        return GitLabEnvironment.GITLAB_COM;
      case GITLAB_DEVELOPMENT_URL:
        return GitLabEnvironment.GITLAB_DEVELOPMENT;
      case GITLAB_STAGING_URL:
        return GitLabEnvironment.GITLAB_STAGING;
      case GITLAB_ORG_URL:
        return GitLabEnvironment.GITLAB_ORG;
      default:
        return GitLabEnvironment.GITLAB_SELF_MANAGED;
    }
  }
}
+17 −7
Original line number Diff line number Diff line
import * as vscode from 'vscode';
import { GitLabChatController } from './gitlab_chat_controller';
import { GitLabPlatformManagerForChat } from './get_platform_manager_for_chat';
import { GitLabEnvironment, GitLabPlatformManagerForChat } from './get_platform_manager_for_chat';
import { GitLabChatRecord } from './gitlab_chat_record';
import { submitFeedback } from './utils/submit_feedback';
import { SubmitFeedbackParams, submitFeedback } from './utils/submit_feedback';
import { SPECIAL_MESSAGES } from './constants';
import { AiCompletionResponseMessageType } from '../api/graphql/ai_completion_response_channel';
import { createFakePartial } from '../test_utils/create_fake_partial';

const apiMock = {
  processNewUserPrompt: jest.fn(),
  pullAiMessage: jest.fn(),
  cleanChat: jest.fn(),
  subscribeToUpdates: jest.fn(),
  getGitLabEnvironment: jest.fn(),
};

jest.mock('./gitlab_chat_api', () => ({
@@ -36,10 +38,13 @@ jest.mock('./gitlab_chat_view', () => ({
}));

describe('GitLabChatController', () => {
  let platformManager: GitLabPlatformManagerForChat;
  let controller: GitLabChatController;

  beforeEach(() => {
    const platformManager = createFakePartial<GitLabPlatformManagerForChat>({
      getGitLabEnvironment: async () => GitLabEnvironment.GITLAB_COM,
    });

    controller = new GitLabChatController(platformManager, {} as vscode.ExtensionContext);
    apiMock.processNewUserPrompt = jest.fn().mockResolvedValue({
      aiAction: {
@@ -307,15 +312,21 @@ describe('GitLabChatController', () => {
  describe('viewMessageHandler', () => {
    describe('trackFeedback', () => {
      it('calls submitFeedback when data is present', async () => {
        const expected: SubmitFeedbackParams = {
          extendedTextFeedback: 'free text',
          feedbackChoices: ['choice1', 'choice2'],
          gitlabEnvironment: GitLabEnvironment.GITLAB_COM,
        };

        await controller.viewMessageHandler({
          eventType: 'trackFeedback',
          data: {
            extendedTextFeedback: 'free text',
            feedbackChoices: ['choice1', 'choice2'],
            extendedTextFeedback: expected.extendedTextFeedback,
            feedbackChoices: expected.feedbackChoices,
          },
        });

        expect(submitFeedback).toHaveBeenCalledWith('free text', ['choice1', 'choice2']);
        expect(submitFeedback).toHaveBeenCalledWith(expected);
      });

      it('does not call submitFeedback when no data is present', async () => {
@@ -326,7 +337,6 @@ describe('GitLabChatController', () => {
        expect(submitFeedback).not.toHaveBeenCalled();
      });
    });

    describe('newPrompt', () => {
      it('processes new record', async () => {
        controller.processNewUserRecord = jest.fn();
+10 −1
Original line number Diff line number Diff line
@@ -14,12 +14,15 @@ export class GitLabChatController implements vscode.WebviewViewProvider {

  readonly #api: GitLabChatApi;

  readonly #manager: GitLabPlatformManagerForChat;

  constructor(manager: GitLabPlatformManagerForChat, context: vscode.ExtensionContext) {
    this.chatHistory = [];
    this.#api = new GitLabChatApi(manager);
    this.#view = new GitLabChatView(context);
    this.#view.onViewMessage(this.viewMessageHandler.bind(this));
    this.#view.onDidBecomeVisible(this.#restoreHistory.bind(this));
    this.#manager = manager;
  }

  async resolveWebviewView(webviewView: vscode.WebviewView) {
@@ -40,7 +43,13 @@ export class GitLabChatController implements vscode.WebviewViewProvider {
      }
      case 'trackFeedback': {
        if (message.data) {
          await submitFeedback(message.data.extendedTextFeedback, message.data.feedbackChoices);
          const gitlabEnvironment = await this.#manager.getGitLabEnvironment();

          await submitFeedback({
            extendedTextFeedback: message.data.extendedTextFeedback,
            feedbackChoices: message.data.feedbackChoices,
            gitlabEnvironment,
          });
        }

        break;
+40 −12
Original line number Diff line number Diff line
import { submitFeedback } from './submit_feedback';
import { GitLabEnvironment } from '../get_platform_manager_for_chat';
import { SubmitFeedbackParams, submitFeedback } from './submit_feedback';

jest.mock('../../snowplow/snowplow', () => ({
  Snowplow: {
@@ -17,15 +18,22 @@ describe('submitFeedback', () => {

  describe('with feedback', () => {
    it('sends snowplow event', async () => {
      await submitFeedback('Freetext feedback', ['helpful', 'fast']);
      const expected: SubmitFeedbackParams = {
        extendedTextFeedback: 'Freetext feedback',
        feedbackChoices: ['helpful', 'fast'],
        gitlabEnvironment: GitLabEnvironment.GITLAB_COM,
      };

      await submitFeedback(expected);

      const standardContext = {
        schema: 'iglu:com.gitlab/gitlab_standard/jsonschema/1-0-9',
        data: {
          extra: {
            extended_feedback: 'Freetext feedback',
          source: 'gitlab-vscode',
          extra: {
            extended_feedback: expected.extendedTextFeedback,
          },
          environment: expected.gitlabEnvironment,
        },
      };

@@ -41,15 +49,22 @@ describe('submitFeedback', () => {
    });

    it('sends snowplow event when choices are null', async () => {
      await submitFeedback('Freetext feedback', null);
      const expected: SubmitFeedbackParams = {
        extendedTextFeedback: 'Freetext feedback',
        feedbackChoices: null,
        gitlabEnvironment: GitLabEnvironment.GITLAB_COM,
      };

      await submitFeedback(expected);

      const standardContext = {
        schema: 'iglu:com.gitlab/gitlab_standard/jsonschema/1-0-9',
        data: {
          extra: {
            extended_feedback: 'Freetext feedback',
          source: 'gitlab-vscode',
          extra: {
            extended_feedback: expected.extendedTextFeedback,
          },
          environment: expected.gitlabEnvironment,
        },
      };

@@ -64,15 +79,22 @@ describe('submitFeedback', () => {
    });

    it('sends snowplow event when free text feedback is null', async () => {
      await submitFeedback(null, ['helpful', 'fast']);
      const expected: SubmitFeedbackParams = {
        extendedTextFeedback: null,
        feedbackChoices: ['helpful', 'fast'],
        gitlabEnvironment: GitLabEnvironment.GITLAB_COM,
      };

      await submitFeedback(expected);

      const standardContext = {
        schema: 'iglu:com.gitlab/gitlab_standard/jsonschema/1-0-9',
        data: {
          extra: {
            extended_feedback: null,
          source: 'gitlab-vscode',
          extra: {
            extended_feedback: expected.extendedTextFeedback,
          },
          environment: expected.gitlabEnvironment,
        },
      };

@@ -89,14 +111,20 @@ describe('submitFeedback', () => {
  });

  describe('with empty feedback', () => {
    const emptySubmitFeedbackParams: SubmitFeedbackParams = {
      extendedTextFeedback: '',
      feedbackChoices: [],
      gitlabEnvironment: GitLabEnvironment.GITLAB_COM,
    };

    it('does not send a snowplow event', async () => {
      await submitFeedback('', []);
      await submitFeedback(emptySubmitFeedbackParams);

      expect(trackStructEvent).not.toHaveBeenCalled();
    });

    it('does not send a snowplow event when free text feedback and choices are null', async () => {
      await submitFeedback(null, null);
      await submitFeedback(emptySubmitFeedbackParams);

      expect(trackStructEvent).not.toHaveBeenCalled();
    });
Loading