api/src/__tests__/activityLogQueries.test.ts

Summary

Maintainability
D
1 day
Test Coverage
import * as faker from 'faker';
import { graphqlRequest } from '../db/connection';
import {
  activityLogFactory,
  brandFactory,
  companyFactory,
  conformityFactory,
  conversationFactory,
  customerFactory,
  dealFactory,
  engageMessageFactory,
  growthHackFactory,
  integrationFactory,
  internalNoteFactory,
  pipelineFactory,
  stageFactory,
  tagsFactory,
  taskFactory,
  ticketFactory,
  userFactory
} from '../db/factories';
import { IntegrationsAPI } from '../data/dataSources';
import * as logUtils from '../data/logUtils';
import './setup.ts';
import * as pluginUtils from '../pluginUtils';

describe('activityLogQueries', () => {
  let brand;
  let integration;
  let deal;
  let ticket;
  let growtHack;
  let task;

  const commonParamDefs = `
    $contentType: String!,
    $contentId: String!,
    $activityType: String,
    $limit: Int,
  `;

  const commonParams = `
    contentType: $contentType
    contentId: $contentId
    activityType: $activityType
    limit: $limit
  `;

  const qryActivityLogs = `
    query activityLogs(${commonParamDefs}) {
      activityLogs(${commonParams}) {
        _id
        action
        contentId
        contentType
        content
        createdAt
        createdBy

        createdByDetail
        contentDetail
        contentTypeDetail
      }
    }
  `;

  const qryActivityLogsByAction = `
    query activityLogsByAction($contentType: String, $action: String, $pipelineId: String) {
      activityLogsByAction(contentType: $contentType, action: $action, pipelineId: $pipelineId) {
        activityLogs {
          _id
          action
          contentId
          contentType
          content
          createdAt
          createdBy
          
          createdUser {
            _id
          }
          contentTypeDetail
        }
        totalCount
      }
    }
  `;

  beforeEach(async () => {
    brand = await brandFactory();
    integration = await integrationFactory({
      brandId: brand._id
    });
    deal = await dealFactory();
    ticket = await ticketFactory();
    growtHack = await growthHackFactory();
    task = await taskFactory();
  });

  test('Activity log', async () => {
    const contentId = faker.random.uuid();
    const contentType = 'customer';

    const spy = jest.spyOn(logUtils, 'fetchLogs');

    spy.mockImplementation(async () => {
      const activityLogs: any[] = [];

      for (let i = 0; i < 3; i++) {
        activityLogs.push(await activityLogFactory({ contentId, contentType }));
      }

      return activityLogs;
    });

    const args = { contentId, contentType };

    const response = await graphqlRequest(
      qryActivityLogs,
      'activityLogs',
      args
    );

    expect(response.length).toBe(3);

    spy.mockRestore();
  });

  test('Activity log content type checklist & checklist', async () => {
    const contentId = faker.random.uuid();

    const spy = jest.spyOn(logUtils, 'fetchLogs');

    spy.mockImplementation(async () => [
      await activityLogFactory({ contentId, contentType: 'checklist' }),
      await activityLogFactory({ contentId, contentType: 'checklistitem' })
    ]);

    const args1 = { contentId, contentType: 'checklist' };
    const args2 = { contentId, contentType: 'checklistitem' };

    const response1 = await graphqlRequest(
      qryActivityLogs,
      'activityLogs',
      args1
    );
    const response2 = await graphqlRequest(
      qryActivityLogs,
      'activityLogs',
      args2
    );

    expect(response1.length).toBe(2);
    expect(response2.length).toBe(2);

    spy.mockRestore();
  });

  test('Activity log with plugin type', async () => {
    const customer = await customerFactory({});

    const spy = jest.spyOn(pluginUtils, 'collectPluginContent');

    spy.mockImplementation(async () => []);

    const activityTypes = [{ type: 'plugin_test' }];

    for (const t of activityTypes) {
      const args = {
        contentId: customer._id,
        contentType: t.type === 'sms' ? 'sms' : 'customer',
        activityType: t.type
      };

      const processState = process.env.ELK_SYNCER;

      process.env.ELK_SYNCER = 'false';

      const response = await graphqlRequest(
        qryActivityLogs,
        'activityLogs',
        args,
        {}
      );

      expect(response).toBeDefined();

      process.env.ELK_SYNCER = processState;
    }

    spy.mockRestore();
  });

  test('Activity log with all activity types', async () => {
    const customer = await customerFactory({});

    await conformityFactory({
      mainType: 'customer',
      mainTypeId: customer._id,
      relType: 'task',
      relTypeId: task._id
    });

    await conversationFactory({ customerId: customer._id });
    await internalNoteFactory({ contentTypeId: customer._id });
    await engageMessageFactory({
      customerIds: [customer._id],
      method: 'email'
    });

    const spy = jest.spyOn(logUtils, 'fetchLogs');

    spy.mockImplementation(async () => [
      await activityLogFactory({ contentId: customer._id, contentType: 'sms' })
    ]);

    const dataSources = { IntegrationsAPI: new IntegrationsAPI() };
    const spy1 = jest.spyOn(dataSources.IntegrationsAPI, 'fetchApi');

    spy1.mockImplementation(() => Promise.resolve());

    const activityTypes = [
      { type: 'conversation', content: 'company' },
      { type: 'email', content: 'email' },
      { type: 'internal_note', content: 'internal_note' },
      { type: 'task', content: 'task' },
      { type: 'sms', content: 'sms sent' },
      { type: 'campaign', content: 'campaign' }
    ];

    for (const t of activityTypes) {
      const args = {
        contentId: customer._id,
        contentType: t.type === 'sms' ? 'sms' : 'customer',
        activityType: t.type
      };

      const processState = process.env.ELK_SYNCER;

      process.env.ELK_SYNCER = 'false';

      const response = await graphqlRequest(
        qryActivityLogs,
        'activityLogs',
        args,
        { dataSources }
      );

      expect(response).toBeDefined();

      process.env.ELK_SYNCER = processState;
    }

    spy.mockRestore();
    spy1.mockRestore();
  });

  test('Activity log with created by user', async () => {
    const user = await userFactory();
    const contentId = faker.random.uuid();
    const contentType = 'customer';
    const createdBy = user._id;

    const doc = {
      contentId,
      contentType,
      createdBy
    };

    const spy = jest.spyOn(logUtils, 'fetchLogs');

    spy.mockImplementation(async () => [await activityLogFactory(doc)]);

    const args = { contentId, contentType };

    const response = await graphqlRequest(
      qryActivityLogs,
      'activityLogs',
      args
    );

    expect(response.length).toBe(1);

    spy.mockRestore();
  });

  test('Activity log with created by integration', async () => {
    const contentId = faker.random.uuid();
    const contentType = 'customer';
    const createdBy = integration._id;

    const doc = {
      contentId,
      contentType,
      createdBy
    };

    const spy = jest.spyOn(logUtils, 'fetchLogs');

    spy.mockImplementation(async () => [await activityLogFactory(doc)]);

    const args = { contentId, contentType };

    const response = await graphqlRequest(
      qryActivityLogs,
      'activityLogs',
      args
    );

    expect(response.length).toBe(1);

    spy.mockRestore();
  });

  test('Activity log content type detail', async () => {
    const createdBy = integration._id;

    const types = [
      { type: 'deal', content: deal },
      { type: 'ticket', content: ticket },
      { type: 'task', content: task },
      { type: 'growthHack', content: growtHack }
    ];

    for (const type of types) {
      const spy = jest.spyOn(logUtils, 'fetchLogs');

      const doc = {
        contentId: type.content._id,
        contentType: type.type,
        createdBy
      };

      spy.mockImplementation(async () => [await activityLogFactory(doc)]);

      const args = { contentId: type.content._id, contentType: type.type };

      const response = await graphqlRequest(
        qryActivityLogs,
        'activityLogs',
        args
      );

      expect(response.length).toBe(1);

      spy.mockRestore();
    }
  });

  test('Activity log content type detail (throw error)', async () => {
    const createdBy = integration._id;

    const spy = jest.spyOn(logUtils, 'fetchLogs');

    const doc = {
      contentId: 'contentId1',
      contentType: 'deal',
      createdBy
    };

    spy.mockImplementation(async () => [await activityLogFactory(doc)]);

    const args = { contentId: 'contentId2', contentType: 'Deal' };

    try {
      await graphqlRequest(qryActivityLogs, 'activityLogs', args);
    } catch (e) {
      expect(e.message).toBe('Deal not found');
    }

    spy.mockRestore();
  });

  test('Activity log action merge', async () => {
    const customer = await customerFactory();
    const company = await companyFactory();

    const types = [
      { type: 'company', content: company },
      { type: 'customer', content: customer }
    ];

    for (const type of types) {
      const spy = jest.spyOn(logUtils, 'fetchLogs');

      const doc = {
        contentId: type.content._id,
        contentType: type.type,
        action: 'merge',
        content: []
      };

      spy.mockImplementation(async () => [await activityLogFactory(doc)]);

      const args = { contentId: type.content._id, contentType: type.type };

      const response = await graphqlRequest(
        qryActivityLogs,
        'activityLogs',
        args
      );

      expect(response.length).toBe(1);

      spy.mockRestore();
    }
  });

  test('Activity log action moved', async () => {
    const stage1 = await stageFactory();
    const stage2 = await stageFactory();

    const types = [
      { type: 'deal', content: deal },
      { type: 'ticket', content: ticket },
      { type: 'task', content: task },
      { type: 'growthHack', content: growtHack }
    ];

    for (const type of types) {
      const spy = jest.spyOn(logUtils, 'fetchLogs');

      const doc1 = {
        contentId: type.content._id,
        contentType: type.type,
        action: 'moved',
        content: {
          oldStageId: stage1._id,
          destinationStageId: stage2._id
        }
      };

      const doc2 = {
        contentId: type.content._id,
        contentType: type.type,
        action: 'moved',
        content: {
          oldStageId: '',
          destinationStageId: ''
        }
      };

      spy.mockImplementation(async () => [
        await activityLogFactory(doc1),
        await activityLogFactory(doc2)
      ]);

      const args = { contentId: type.content._id, contentType: type.type };

      const response = await graphqlRequest(
        qryActivityLogs,
        'activityLogs',
        args
      );

      expect(response.length).toBe(2);

      spy.mockRestore();
    }
  });

  test('Activity log action convert', async () => {
    const types = [{ type: 'task', content: task }];

    for (const type of types) {
      const spy = jest.spyOn(logUtils, 'fetchLogs');

      const doc = {
        contentId: type.content._id,
        contentType: type.type,
        action: 'convert'
      };

      spy.mockImplementation(async () => [await activityLogFactory(doc)]);

      const args = { contentId: type.content._id, contentType: type.type };

      const response = await graphqlRequest(
        qryActivityLogs,
        'activityLogs',
        args
      );

      expect(response.length).toBe(1);

      spy.mockRestore();
    }
  });

  test('Activity log action assignee', async () => {
    const deal1 = await dealFactory();

    const doc = {
      contentId: deal1._id,
      contentType: 'deal',
      action: 'assignee',
      content: {
        addedUserIds: [],
        removedUserIds: []
      }
    };

    const spy = jest.spyOn(logUtils, 'fetchLogs');
    spy.mockImplementation(async () => [await activityLogFactory(doc)]);

    const args = { contentId: deal1._id, contentType: 'deal' };

    const response = await graphqlRequest(
      qryActivityLogs,
      'activityLogs',
      args
    );

    expect(response.length).toBe(1);

    spy.mockRestore();
  });

  test('Activity log tagged', async () => {
    const customer = await customerFactory();
    const tag = await tagsFactory();

    const doc = {
      contentId: customer._id,
      contentType: 'customer',
      action: 'tagged',
      content: {
        tagIds: [tag._id]
      }
    };

    const spy = jest.spyOn(logUtils, 'fetchLogs');
    spy.mockImplementation(async () => [await activityLogFactory(doc)]);

    const args = { contentId: customer._id, contentType: 'customer' };

    const response = await graphqlRequest(
      qryActivityLogs,
      'activityLogs',
      args
    );

    expect(response.length).toBe(1);

    spy.mockRestore();
  });

  test('Activity log by action', async () => {
    const pipeline = await pipelineFactory();
    const stage = await stageFactory({ pipelineId: pipeline._id });
    const mydeal = await dealFactory({ stageId: stage._id });

    const doc = {
      contentId: mydeal._id,
      contentType: 'deal',
      action: 'create'
    };

    const spy = jest.spyOn(logUtils, 'fetchLogs');
    spy.mockImplementation(async () => ({
      activityLogs: [await activityLogFactory(doc)],
      totalCount: 1
    }));

    const args = {
      contentType: 'deal',
      action: 'create',
      pipelineId: pipeline._id
    };

    const response = await graphqlRequest(
      qryActivityLogsByAction,
      'activityLogsByAction',
      args
    );

    expect(response.activityLogs.length).toBe(1);

    spy.mockRestore();
  });

  test('Activity log by action (delete)', async () => {
    const pipeline = await pipelineFactory();
    const stage = await stageFactory({ pipelineId: pipeline._id });
    const mydeal = await dealFactory({ stageId: stage._id });

    const doc = {
      contentId: mydeal._id,
      contentType: 'deal',
      action: 'delete'
    };

    const spy = jest.spyOn(logUtils, 'fetchLogs');
    spy.mockImplementation(async () => ({
      logs: [await activityLogFactory(doc)]
    }));

    const args = {
      contentType: 'deal',
      action: 'delete',
      pipelineId: pipeline._id
    };

    const response = await graphqlRequest(
      qryActivityLogsByAction,
      'activityLogsByAction',
      args
    );

    expect(response.activityLogs.length).toBe(1);

    spy.mockRestore();
  });

  test('Activity log by action (add note)', async () => {
    const pipeline = await pipelineFactory();
    const stage = await stageFactory({ pipelineId: pipeline._id });
    const mydeal = await dealFactory({ stageId: stage._id });

    await internalNoteFactory({
      contentTypeId: mydeal._id,
      contentType: 'deal'
    });

    const args = {
      contentType: 'deal',
      action: 'addNote',
      pipelineId: pipeline._id
    };

    const response = await graphqlRequest(
      qryActivityLogsByAction,
      'activityLogsByAction',
      args
    );

    expect(response.activityLogs.length).toBe(1);
  });

  test('Activity log by action (empty action)', async () => {
    const args = { action: '' };

    const response = await graphqlRequest(
      qryActivityLogsByAction,
      'activityLogsByAction',
      args
    );

    expect(response.activityLogs.length).toBe(0);
  });
});