<|start_filename|>packages/gatsby-theme-newrelic/src/hooks/__tests__/useInstrumentedHandler.js<|end_filename|>
import useInstrumentedHandler from '../useInstrumentedHandler';
import { renderHook } from '@testing-library/react-hooks';

const originalError = console.error;

global.newrelic = {
  addPageAction: jest.fn(),
};

afterEach(() => {
  console.error = originalError;
  global.newrelic.addPageAction.mockClear();
});

test('instruments page action and calls original handler with all arguments', () => {
  const originalHandler = jest.fn();
  const { result } = renderHook(() =>
    useInstrumentedHandler(originalHandler, { actionName: 'click' })
  );

  result.current(1, 2);

  expect(originalHandler).toHaveBeenCalledTimes(1);
  expect(originalHandler).toHaveBeenCalledWith(1, 2);
  expect(global.newrelic.addPageAction).toHaveBeenCalledTimes(1);
  expect(global.newrelic.addPageAction).toHaveBeenCalledWith('click', {});
});

test('attaches any additional fields in config as attributes', () => {
  const { result } = renderHook(() =>
    useInstrumentedHandler(jest.fn(), { actionName: 'click', darkMode: true })
  );

  result.current();

  expect(global.newrelic.addPageAction).toHaveBeenCalledWith('click', {
    darkMode: true,
  });
});

test('original return value is maintained', () => {
  const originalHandler = () => 'tacos';
  const { result } = renderHook(() =>
    useInstrumentedHandler(originalHandler, { actionName: 'click' })
  );

  const returnValue = result.current();

  expect(returnValue).toEqual('tacos');
});

test('successfully instruments call if original handler is not set', () => {
  const { result } = renderHook(() =>
    useInstrumentedHandler(null, { actionName: 'click' })
  );

  result.current('clicked!');

  expect(global.newrelic.addPageAction).toHaveBeenCalledWith('click', {});
});

test('allows config argument to be a function called with the handler arguments', () => {
  const { result } = renderHook(() =>
    useInstrumentedHandler(jest.fn(), (a, b) => ({
      actionName: 'add',
      sum: a + b,
    }))
  );

  result.current(2, 2);

  expect(global.newrelic.addPageAction).toHaveBeenCalledWith('add', { sum: 4 });
});

test('does not instrument the request if newrelic is not installed', () => {
  const originalNewRelic = global.newrelic;
  global.newrelic = null;

  const originalHandler = jest.fn();
  const { result } = renderHook(() =>
    useInstrumentedHandler(originalHandler, { actionName: 'not called' })
  );

  expect(() => result.current()).not.toThrow();
  expect(originalHandler).toHaveBeenCalled();

  global.newrelic = originalNewRelic;
});

test('warns if actionName is not set', () => {
  console.error = jest.fn();

  const { result } = renderHook(() =>
    useInstrumentedHandler(jest.fn(), { name: 'click' })
  );

  result.current();

  expect(console.error).toHaveBeenCalledWith(
    expect.stringContaining(
      'You are attempting to instrument a handler, but the `actionName` property is not set. This will result in a no-op.'
    )
  );
});

test('does not warn if newrelic is not installed', () => {
  const originalNewRelic = global.newrelic;

  global.newrelic = null;
  console.error = jest.fn();

  const { result } = renderHook(() =>
    useInstrumentedHandler(jest.fn(), { name: 'click' })
  );

  result.current();

  expect(console.error).not.toHaveBeenCalled();

  global.newrelic = originalNewRelic;
});


<|start_filename|>packages/gatsby-theme-newrelic/src/hooks/useInstrumentedData.js<|end_filename|>
import { useEffect } from 'react';
import useDeepMemo from './useDeepMemo';
import useWarning from './useWarning';

const useInstrumentedData = (data, { enabled = true } = {}) => {
  const memoizedData = useDeepMemo(() => data, [data]);

  useWarning(
    memoizedData.actionName,
    'useInstrumentedData: You are attempting to instrument data, but the `actionName` property is not set. This will result in a no-op.',
    { once: false }
  );

  useEffect(() => {
    const { actionName, ...attributes } = memoizedData;

    if (actionName && enabled && window.newrelic) {
      window.newrelic.addPageAction(actionName, attributes);
    }
  }, [enabled, memoizedData]);
};

export default useInstrumentedData;


<|start_filename|>packages/gatsby-theme-newrelic/src/hooks/__tests__/useInstrumentedData.js<|end_filename|>
import useInstrumentedData from '../useInstrumentedData';
import { renderHook } from '@testing-library/react-hooks/pure';

const originalError = console.error;

global.newrelic = {
  addPageAction: jest.fn(),
};

afterEach(() => {
  console.error = originalError;
  global.newrelic.addPageAction.mockClear();
});

test('instruments data with newrelic', () => {
  renderHook(() => useInstrumentedData({ actionName: 'click' }));

  expect(global.newrelic.addPageAction).toHaveBeenCalledTimes(1);
  expect(global.newrelic.addPageAction).toHaveBeenCalledWith('click', {});
});

test('instruments additional fields as attributes', () => {
  renderHook(() =>
    useInstrumentedData({ actionName: 'click', location: '/page-1' })
  );

  expect(global.newrelic.addPageAction).toHaveBeenCalledTimes(1);
  expect(global.newrelic.addPageAction).toHaveBeenCalledWith('click', {
    location: '/page-1',
  });
});

test('only sends another page action when data has changed', () => {
  const { rerender } = renderHook(
    (data) => useInstrumentedData({ actionName: 'click', ...data }),
    {
      initialProps: {
        darkMode: true,
      },
    }
  );

  expect(global.newrelic.addPageAction).toHaveBeenCalledTimes(1);

  rerender({ darkMode: true });

  expect(global.newrelic.addPageAction).toHaveBeenCalledTimes(1);

  rerender({ darkMode: false });

  expect(global.newrelic.addPageAction).toHaveBeenCalledTimes(2);
  expect(global.newrelic.addPageAction).toHaveBeenLastCalledWith('click', {
    darkMode: false,
  });
});

test('allows the effect to be turned off', () => {
  renderHook(() =>
    useInstrumentedData(
      { actionName: 'click', location: '/page-1' },
      { enabled: false }
    )
  );

  expect(global.newrelic.addPageAction).not.toHaveBeenCalled();
});

test('handles toggling the effect on/off', () => {
  const { rerender } = renderHook(
    ({ enabled }) =>
      useInstrumentedData(
        { actionName: 'click', location: '/page-1' },
        { enabled }
      ),
    { initialProps: { enabled: false } }
  );

  expect(global.newrelic.addPageAction).not.toHaveBeenCalled();

  rerender({ enabled: true });

  expect(global.newrelic.addPageAction).toHaveBeenCalledTimes(1);

  rerender({ enabled: false });

  expect(global.newrelic.addPageAction).toHaveBeenCalledTimes(1);
});

test('does not instrument if newrelic is not installed', () => {
  const originalNewRelic = global.newrelic;
  global.newrelic = null;
  console.error = jest.fn();

  const { result } = renderHook(() =>
    useInstrumentedData({ actionName: 'click' })
  );

  expect(result.error).toBeUndefined();

  global.newrelic = originalNewRelic;
});

test('warns if actionName is not set', () => {
  console.error = jest.fn();

  renderHook(() => useInstrumentedData({ title: 'New Relic' }));

  expect(console.error).toHaveBeenLastCalledWith(
    expect.stringContaining(
      'useInstrumentedData: You are attempting to instrument data, but the `actionName` property is not set. This will result in a no-op.'
    )
  );
});

test('does not instrument if action name is not set', () => {
  console.error = jest.fn();

  renderHook(() => useInstrumentedData({ title: 'New Relic' }));

  expect(global.newrelic.addPageAction).not.toHaveBeenCalled();
});


<|start_filename|>packages/gatsby-theme-newrelic/src/hooks/useTessen.js<|end_filename|>
import { useMemo } from 'react';
import { useStaticQuery, graphql } from 'gatsby';
import createTessen from '../utils/createTessen';

const useTessen = () => {
  const {
    newRelicThemeConfig: { tessen: config },
  } = useStaticQuery(graphql`
    query {
      newRelicThemeConfig {
        tessen {
          product
          subproduct
        }
      }
    }
  `);

  const tessen = useMemo(() => createTessen(config), [config]);

  return tessen;
};

export default useTessen;


<|start_filename|>packages/gatsby-theme-newrelic/src/hooks/useInstrumentedHandler.js<|end_filename|>
import { useCallback, useEffect, useRef } from 'react';
import warning from 'warning';

const useInstrumentedHandler = (handler, attributes) => {
  const savedHandler = useRef();

  useEffect(() => {
    savedHandler.current = handler;
  }, [handler]);

  return useCallback(
    (...args) => {
      const { actionName, ...attrs } =
        typeof attributes === 'function' ? attributes(...args) : attributes;

      if (window.newrelic) {
        warning(
          actionName,
          'You are attempting to instrument a handler, but the `actionName` property is not set. This will result in a no-op.'
        );

        actionName && window.newrelic.addPageAction(actionName, attrs);
      }

      if (savedHandler.current) {
        return savedHandler.current(...args);
      }
    },
    [attributes]
  );
};

export default useInstrumentedHandler;
