import './helpers/mockRequestAtlassian'; import api from '../../index'; import { MOCK_BASE_COMPONENT, MOCK_BASE_COMPONENT_WITH_ID, MOCK_CHANGED_COMPONENT, MOCK_CHANGED_COMPONENT_WITH_CUSTOM_FIELDS, MOCK_CHANGED_COMPONENT_WITH_LIFECYCLE_FIELD, MOCK_COMPLEX_COMPONENT, MOCK_COMPONENT_ID, MOCK_COMPONENT_WITH_DATA_MANAGER, MOCK_COMPONENT_WITH_EMPTY_DATA_MANAGER, MOCK_COMPONENT_WITH_NULL_DATA_MANAGER, MOCK_GET_CHANGED_COMPONENT, MOCK_GET_CHANGED_COMPONENT_WITH_CUSTOM_FIELDS, MOCK_GET_CHANGED_COMPONENT_WITH_LIFECYCLE_FIELD, MOCK_GET_COMPLEX_COMPONENT, MOCK_GET_COMPONENT_WITH_CHANGED_TYPEID_FIELD, MOCK_GET_COMPONENT_WITH_DATA_MANAGER, MOCK_GET_COMPONENT_WITH_NULL_DATA_MANAGER, MOCK_GET_LINK, MOCK_GET_LINK_2, MOCK_GQL_ERROR, MOCK_LABEL, MOCK_LABEL_2, MOCK_MUTATION_ERROR, MOCK_MUTATION_ERROR_MESSAGE, MOCK_MUTATION_ERROR_SINGLE, MOCK_RELATIONSHIP, MOCK_RELATIONSHIP_2, } from '../fixtures/mocks'; import { COMPOUND_MUTATION_NAME } from '../../helpers'; import { mockGetComponent, mockRequestGraph, mockUpdateDataManager, } from './helpers/requestMocks'; import { findCall } from './helpers/matchGql'; import { fgqlActionSubjects, fgqlActions, fgqlSources, } from '../../helpers/analyticTypes'; const compassApp = api.compass.asApp(); const getComponentSpy = jest.spyOn(compassApp, 'getComponent'); const updateDataManagerSpy = jest.spyOn(compassApp, 'updateDataManager'); // @ts-ignore const requestGraphSpy = jest.spyOn(compassApp.api, 'requestGraph'); describe('updateComponent', () => { beforeEach(() => { jest.resetAllMocks(); jest.spyOn(global.Math, 'random').mockReturnValue(0.0000000001); }); test('successfully updates base component, links, relationships, labels', async () => { mockRequestGraph(requestGraphSpy); // gets resp for updateComponent mockGetComponent(getComponentSpy, MOCK_GET_CHANGED_COMPONENT); const updatedComponentResp = await compassApp.updateComponent({ id: MOCK_COMPONENT_ID, currentComponent: MOCK_BASE_COMPONENT_WITH_ID, ...MOCK_CHANGED_COMPONENT, }); expect(findCall(requestGraphSpy, COMPOUND_MUTATION_NAME)).toMatchSnapshot(); expect(updatedComponentResp).toEqual({ errors: [], success: true, data: { component: MOCK_GET_CHANGED_COMPONENT }, // todo check this }); }); test('successfully updates base component with lifecycle field', async () => { mockRequestGraph(requestGraphSpy); // gets resp for updateComponent mockGetComponent( getComponentSpy, MOCK_GET_CHANGED_COMPONENT_WITH_LIFECYCLE_FIELD, ); const updatedComponentResp = await compassApp.updateComponent({ id: MOCK_COMPONENT_ID, currentComponent: MOCK_BASE_COMPONENT_WITH_ID, ...MOCK_CHANGED_COMPONENT_WITH_LIFECYCLE_FIELD, }); expect(updatedComponentResp).toEqual({ errors: [], success: true, data: { component: MOCK_GET_CHANGED_COMPONENT_WITH_LIFECYCLE_FIELD }, }); }); test('successfully updates base component with typeId field', async () => { mockRequestGraph(requestGraphSpy); // gets resp for updateComponent mockGetComponent( getComponentSpy, MOCK_GET_COMPONENT_WITH_CHANGED_TYPEID_FIELD, ); const updatedComponentResp = await compassApp.updateComponent({ id: MOCK_COMPONENT_ID, currentComponent: MOCK_BASE_COMPONENT_WITH_ID, ...MOCK_GET_COMPONENT_WITH_CHANGED_TYPEID_FIELD, }); expect(updatedComponentResp).toEqual({ errors: [], success: true, data: { component: MOCK_GET_COMPONENT_WITH_CHANGED_TYPEID_FIELD }, }); }); test('successfully updates base component with custom fields', async () => { mockRequestGraph(requestGraphSpy); // gets resp for updateComponent mockGetComponent( getComponentSpy, MOCK_GET_CHANGED_COMPONENT_WITH_CUSTOM_FIELDS, ); const updatedComponentResp = await compassApp.updateComponent({ id: MOCK_COMPONENT_ID, currentComponent: MOCK_BASE_COMPONENT_WITH_ID, ...MOCK_CHANGED_COMPONENT_WITH_CUSTOM_FIELDS, }); expect(findCall(requestGraphSpy, COMPOUND_MUTATION_NAME)).toMatchSnapshot(); expect(updatedComponentResp).toEqual({ errors: [], success: true, data: { component: MOCK_GET_CHANGED_COMPONENT_WITH_CUSTOM_FIELDS }, // todo check this }); }); test('returns gql error', async () => { mockRequestGraph(requestGraphSpy, [MOCK_GQL_ERROR]); // gets resp for updateComponent mockGetComponent(getComponentSpy, { id: MOCK_COMPONENT_ID, ...MOCK_BASE_COMPONENT, }); const updatedComponentResp = await compassApp.updateComponent({ id: MOCK_COMPONENT_ID, currentComponent: MOCK_BASE_COMPONENT_WITH_ID, ...MOCK_CHANGED_COMPONENT, }); expect(findCall(requestGraphSpy, COMPOUND_MUTATION_NAME)).toMatchSnapshot(); expect(updatedComponentResp).toMatchSnapshot(); }); test('returns mutation error', async () => { mockRequestGraph(requestGraphSpy, [], { compass: { updateComponent_1: { errors: [MOCK_MUTATION_ERROR], }, }, } as any); // gets resp for updateComponent mockGetComponent(getComponentSpy, { id: MOCK_COMPONENT_ID, ...MOCK_BASE_COMPONENT, }); const updatedComponentResp = await compassApp.updateComponent({ id: MOCK_COMPONENT_ID, currentComponent: MOCK_BASE_COMPONENT_WITH_ID, ...MOCK_CHANGED_COMPONENT, }); expect(findCall(requestGraphSpy, COMPOUND_MUTATION_NAME)).toMatchSnapshot(); expect(updatedComponentResp).toMatchSnapshot(); }); test('returns mutation error with single extension data', async () => { mockRequestGraph(requestGraphSpy, [], { compass: { updateComponent_1: { errors: [MOCK_MUTATION_ERROR_SINGLE], }, }, } as any); mockGetComponent(getComponentSpy, { id: MOCK_COMPONENT_ID, ...MOCK_BASE_COMPONENT, }); const updatedComponentResp = await compassApp.updateComponent({ id: MOCK_COMPONENT_ID, currentComponent: MOCK_BASE_COMPONENT_WITH_ID, ...MOCK_CHANGED_COMPONENT, }); expect(updatedComponentResp).toMatchSnapshot(); }); test('returns default mutation error', async () => { mockRequestGraph(requestGraphSpy, [], { compass: { updateComponent_1: { errors: [{ message: MOCK_MUTATION_ERROR_MESSAGE }], }, }, } as any); // gets resp for updateComponent mockGetComponent(getComponentSpy, { id: MOCK_COMPONENT_ID, ...MOCK_BASE_COMPONENT, }); const updatedComponentResp = await compassApp.updateComponent({ id: MOCK_COMPONENT_ID, currentComponent: MOCK_BASE_COMPONENT_WITH_ID, ...MOCK_CHANGED_COMPONENT, }); expect(findCall(requestGraphSpy, COMPOUND_MUTATION_NAME)).toMatchSnapshot(); expect(updatedComponentResp).toMatchSnapshot(); }); describe('does not update when value passed in is undefined', () => { test('does not update description on base component when description is undefined', async () => { const newComponent = { ...MOCK_BASE_COMPONENT_WITH_ID, description: undefined as any, }; mockRequestGraph(requestGraphSpy); mockGetComponent(getComponentSpy, newComponent); await compassApp.updateComponent({ id: MOCK_COMPONENT_ID, currentComponent: MOCK_BASE_COMPONENT_WITH_ID, ...newComponent, }); expect( findCall(requestGraphSpy, COMPOUND_MUTATION_NAME), ).toMatchSnapshot(); }); test('does not update slug on base component when slug is undefined', async () => { const newComponent = { ...MOCK_BASE_COMPONENT_WITH_ID, description: undefined as any, slug: undefined as any, }; mockRequestGraph(requestGraphSpy); mockGetComponent(getComponentSpy, newComponent); await compassApp.updateComponent({ id: MOCK_COMPONENT_ID, currentComponent: MOCK_BASE_COMPONENT_WITH_ID, ...newComponent, }); expect( findCall(requestGraphSpy, COMPOUND_MUTATION_NAME), ).toMatchSnapshot(); }); test('does not update links, relationships, and labels when undefined', async () => { const newComponent = { ...MOCK_BASE_COMPONENT_WITH_ID, links: undefined as any, relationships: undefined as any, labels: undefined as any, }; mockRequestGraph(requestGraphSpy); mockGetComponent(getComponentSpy, newComponent); await compassApp.updateComponent({ id: MOCK_COMPONENT_ID, currentComponent: { ...MOCK_BASE_COMPONENT_WITH_ID, links: [MOCK_GET_LINK, MOCK_GET_LINK_2], relationships: [MOCK_RELATIONSHIP, MOCK_RELATIONSHIP_2], labels: [MOCK_LABEL, MOCK_LABEL_2], }, ...newComponent, }); expect( findCall(requestGraphSpy, COMPOUND_MUTATION_NAME), ).toMatchSnapshot(); }); }); describe('retrieves component if it is not passed in', () => { test('retrieves a component if it does not exist', async () => { // initial retrieval mockGetComponent(getComponentSpy, MOCK_BASE_COMPONENT_WITH_ID); mockRequestGraph(requestGraphSpy); // updateComponent resp mockGetComponent(getComponentSpy, MOCK_GET_CHANGED_COMPONENT); const updatedComponentResp = await compassApp.updateComponent({ ...MOCK_CHANGED_COMPONENT, }); expect(getComponentSpy).toMatchSnapshot(); expect( findCall(requestGraphSpy, COMPOUND_MUTATION_NAME), ).toMatchSnapshot(); expect(updatedComponentResp).toEqual({ errors: [], success: true, data: { component: MOCK_GET_CHANGED_COMPONENT }, }); }); test('if there is an error with getting a component, it does not try to update', async () => { const GET_COMPONENT_ERROR = 'Could not find component'; mockRequestGraph(requestGraphSpy); mockGetComponent(getComponentSpy, MOCK_BASE_COMPONENT_WITH_ID, true, [ { message: GET_COMPONENT_ERROR }, ]); const updatedComponentResp = await compassApp.updateComponent({ ...MOCK_CHANGED_COMPONENT, }); expect(requestGraphSpy).not.toBeCalled(); expect(updatedComponentResp).toEqual({ errors: [{ message: GET_COMPONENT_ERROR }], success: true, data: { component: {} }, }); }); }); describe('attaches, detaches, and updates data manager', () => { test('successfully updates data manager and returns error if there is an error when updating component', async () => { mockRequestGraph(requestGraphSpy, [MOCK_GQL_ERROR]); mockUpdateDataManager(updateDataManagerSpy); mockGetComponent(getComponentSpy, null); const updatedComponentResp = await compassApp.updateComponent({ id: MOCK_COMPONENT_ID, currentComponent: MOCK_GET_COMPONENT_WITH_DATA_MANAGER, ...MOCK_COMPONENT_WITH_DATA_MANAGER, }); expect( findCall(requestGraphSpy, COMPOUND_MUTATION_NAME), ).toMatchSnapshot(); expect(updateDataManagerSpy.mock.calls).toMatchSnapshot(); expect(updatedComponentResp).toMatchSnapshot(); }); test('successfully updates data manager if there are no errors', async () => { mockRequestGraph(requestGraphSpy); mockUpdateDataManager(updateDataManagerSpy); mockGetComponent(getComponentSpy, MOCK_GET_COMPONENT_WITH_DATA_MANAGER); const updatedComponentResp = await compassApp.updateComponent({ id: MOCK_COMPONENT_ID, currentComponent: MOCK_GET_COMPONENT_WITH_DATA_MANAGER, ...MOCK_COMPONENT_WITH_DATA_MANAGER, }); expect( findCall(requestGraphSpy, COMPOUND_MUTATION_NAME), ).toMatchSnapshot(); expect(updatedComponentResp).toEqual({ errors: [], success: true, data: { component: MOCK_GET_COMPONENT_WITH_DATA_MANAGER, }, }); }); test('adds data manager if one does not exist', async () => { mockRequestGraph(requestGraphSpy); mockUpdateDataManager(updateDataManagerSpy); mockGetComponent(getComponentSpy, MOCK_GET_COMPONENT_WITH_DATA_MANAGER); const updatedComponentResp = await compassApp.updateComponent({ id: MOCK_COMPONENT_ID, currentComponent: MOCK_GET_COMPLEX_COMPONENT, ...MOCK_COMPONENT_WITH_DATA_MANAGER, }); expect( findCall(requestGraphSpy, COMPOUND_MUTATION_NAME), ).toMatchSnapshot(); expect(updatedComponentResp).toEqual({ errors: [], success: true, data: { component: MOCK_GET_COMPONENT_WITH_DATA_MANAGER, }, }); }); test('detaches data manager if one is specified as {}', async () => { mockRequestGraph(requestGraphSpy); mockUpdateDataManager(updateDataManagerSpy); mockGetComponent( getComponentSpy, MOCK_GET_COMPONENT_WITH_NULL_DATA_MANAGER, ); const updatedComponentResp = await compassApp.updateComponent({ id: MOCK_COMPONENT_ID, currentComponent: MOCK_GET_COMPONENT_WITH_DATA_MANAGER, ...MOCK_COMPONENT_WITH_EMPTY_DATA_MANAGER, }); expect( findCall(requestGraphSpy, COMPOUND_MUTATION_NAME), ).toMatchSnapshot(); expect(updatedComponentResp).toEqual({ errors: [], success: true, data: { component: MOCK_GET_COMPONENT_WITH_NULL_DATA_MANAGER, }, }); }); test('detaches data manager if one is specified as null', async () => { mockRequestGraph(requestGraphSpy); mockUpdateDataManager(updateDataManagerSpy); mockGetComponent( getComponentSpy, MOCK_GET_COMPONENT_WITH_NULL_DATA_MANAGER, ); const updatedComponentResp = await compassApp.updateComponent({ id: MOCK_COMPONENT_ID, currentComponent: MOCK_GET_COMPONENT_WITH_DATA_MANAGER, ...MOCK_COMPONENT_WITH_NULL_DATA_MANAGER, }); expect( findCall(requestGraphSpy, COMPOUND_MUTATION_NAME), ).toMatchSnapshot(); expect(updatedComponentResp).toEqual({ errors: [], success: true, data: { component: MOCK_GET_COMPONENT_WITH_NULL_DATA_MANAGER, }, }); }); test('does not try to attach/detach if old component did not have a data manager and new data manager is empty', async () => { mockRequestGraph(requestGraphSpy); mockUpdateDataManager(updateDataManagerSpy); mockGetComponent( getComponentSpy, MOCK_GET_COMPONENT_WITH_NULL_DATA_MANAGER, ); const updatedComponentResp = await compassApp.updateComponent({ id: MOCK_COMPONENT_ID, currentComponent: MOCK_GET_COMPLEX_COMPONENT, ...MOCK_COMPONENT_WITH_EMPTY_DATA_MANAGER, }); expect( findCall(requestGraphSpy, COMPOUND_MUTATION_NAME), ).toMatchSnapshot(); expect(updatedComponentResp).toEqual({ errors: [], success: true, data: { component: MOCK_GET_COMPONENT_WITH_NULL_DATA_MANAGER, }, }); }); test('does not attach/detach (but does update) if old component did have data manager and new manager is empty', async () => { mockRequestGraph(requestGraphSpy); mockUpdateDataManager(updateDataManagerSpy); mockGetComponent( getComponentSpy, MOCK_GET_COMPONENT_WITH_NULL_DATA_MANAGER, ); const updatedComponentResp = await compassApp.updateComponent({ id: MOCK_COMPONENT_ID, currentComponent: MOCK_GET_COMPONENT_WITH_DATA_MANAGER, ...MOCK_COMPLEX_COMPONENT, }); expect( findCall(requestGraphSpy, COMPOUND_MUTATION_NAME), ).toMatchSnapshot(); expect(updatedComponentResp).toEqual({ errors: [], success: true, data: { component: MOCK_GET_COMPONENT_WITH_NULL_DATA_MANAGER, }, }); }); }); describe('updatedFromFile analytics', () => { test('successfully sends updatedFromFile analytics when options are provided', async () => { mockRequestGraph(requestGraphSpy); mockUpdateDataManager(updateDataManagerSpy); mockGetComponent(getComponentSpy, MOCK_GET_CHANGED_COMPONENT); await compassApp.updateComponent({ id: MOCK_COMPONENT_ID, currentComponent: MOCK_BASE_COMPONENT_WITH_ID, ...MOCK_CHANGED_COMPONENT, options: { updatedFromFile: { action: fgqlActions.updated, actionSubject: fgqlActionSubjects.component, source: fgqlSources.configAsCode, }, }, }); // Check that the analytics header was passed correctly const callArgs = requestGraphSpy.mock.calls[0]; const analyticsHeader = callArgs[3]; expect(analyticsHeader).toBeDefined(); expect(analyticsHeader.updatedFromFile).toBeDefined(); const parsedAnalytics = JSON.parse(analyticsHeader.updatedFromFile); expect(parsedAnalytics).toMatchObject({ action: fgqlActions.updated, actionSubject: fgqlActionSubjects.component, source: fgqlSources.configAsCode, }); }); test('does not send updatedFromFile analytics when options are not provided', async () => { mockRequestGraph(requestGraphSpy); mockUpdateDataManager(updateDataManagerSpy); mockGetComponent(getComponentSpy, MOCK_GET_CHANGED_COMPONENT); await compassApp.updateComponent({ id: MOCK_COMPONENT_ID, currentComponent: MOCK_BASE_COMPONENT_WITH_ID, ...MOCK_CHANGED_COMPONENT, }); // Check that no analytics header was passed const callArgs = requestGraphSpy.mock.calls[0]; const analyticsHeader = callArgs[3]; expect(analyticsHeader).toEqual({}); }); }); });