summaryrefslogtreecommitdiff
path: root/spec/frontend/vue_shared/components/url_sync_spec.js
blob: 30a7439579f0029a899746225da13e874e34f9a7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
import { shallowMount } from '@vue/test-utils';
import { historyPushState, historyReplaceState } from '~/lib/utils/common_utils';
import { mergeUrlParams, setUrlParams } from '~/lib/utils/url_utility';
import UrlSyncComponent, {
  URL_SET_PARAMS_STRATEGY,
  HISTORY_REPLACE_UPDATE_METHOD,
} from '~/vue_shared/components/url_sync.vue';

jest.mock('~/lib/utils/url_utility', () => ({
  mergeUrlParams: jest.fn((query, url) => `urlParams: ${JSON.stringify(query)} ${url}`),
  setUrlParams: jest.fn((query, url) => `urlParams: ${JSON.stringify(query)} ${url}`),
}));

jest.mock('~/lib/utils/common_utils', () => ({
  historyPushState: jest.fn(),
  historyReplaceState: jest.fn(),
}));

describe('url sync component', () => {
  let wrapper;
  const mockQuery = { group_id: '5014437163714', project_ids: ['5014437608314'] };

  const findButton = () => wrapper.find('button');

  const createComponent = ({ props = {}, scopedSlots, slots } = {}) => {
    wrapper = shallowMount(UrlSyncComponent, {
      propsData: {
        query: mockQuery,
        ...props,
      },
      scopedSlots,
      slots,
    });
  };

  afterEach(() => {
    wrapper.destroy();
  });

  const expectUrlSyncWithMergeUrlParams = (
    query,
    times,
    mergeUrlParamsReturnValue,
    historyMethod = historyPushState,
  ) => {
    expect(mergeUrlParams).toHaveBeenCalledTimes(times);
    expect(mergeUrlParams).toHaveBeenCalledWith(query, window.location.href, {
      spreadArrays: true,
    });

    expect(historyMethod).toHaveBeenCalledTimes(times);
    expect(historyMethod).toHaveBeenCalledWith(mergeUrlParamsReturnValue);
  };

  const expectUrlSyncWithSetUrlParams = (query, times, setUrlParamsReturnValue) => {
    expect(setUrlParams).toHaveBeenCalledTimes(times);
    expect(setUrlParams).toHaveBeenCalledWith(query, window.location.href, true, true, true);

    expect(historyPushState).toHaveBeenCalledTimes(times);
    expect(historyPushState).toHaveBeenCalledWith(setUrlParamsReturnValue);
  };

  describe('with query as a props', () => {
    it('immediately syncs the query to the URL', () => {
      createComponent();

      expectUrlSyncWithMergeUrlParams(mockQuery, 1, mergeUrlParams.mock.results[0].value);
    });

    describe('when the query is modified', () => {
      const newQuery = { foo: true };

      it('updates the URL with the new query', async () => {
        createComponent();
        // using setProps to test the watcher
        await wrapper.setProps({ query: newQuery });

        expectUrlSyncWithMergeUrlParams(mockQuery, 2, mergeUrlParams.mock.results[1].value);
      });
    });
  });

  describe('with url-params-update-strategy equals to URL_SET_PARAMS_STRATEGY', () => {
    it('uses setUrlParams to generate URL', () => {
      createComponent({
        props: {
          urlParamsUpdateStrategy: URL_SET_PARAMS_STRATEGY,
        },
      });

      expectUrlSyncWithSetUrlParams(mockQuery, 1, setUrlParams.mock.results[0].value);
    });
  });

  describe('with history-update-method equals to HISTORY_REPLACE_UPDATE_METHOD', () => {
    it('uses historyReplaceState to update the URL', () => {
      createComponent({
        props: {
          historyUpdateMethod: HISTORY_REPLACE_UPDATE_METHOD,
        },
      });

      expectUrlSyncWithMergeUrlParams(
        mockQuery,
        1,
        mergeUrlParams.mock.results[0].value,
        historyReplaceState,
      );
    });
  });

  describe('with scoped slot', () => {
    const scopedSlots = {
      default: `
        <button @click="props.updateQuery({bar: 'baz'})">Update Query </button>
        `,
    };

    it('renders the scoped slot', () => {
      createComponent({ props: { query: null }, scopedSlots });

      expect(findButton().exists()).toBe(true);
    });

    it('syncs the url with the scoped slots function', () => {
      createComponent({ props: { query: null }, scopedSlots });

      findButton().trigger('click');

      expectUrlSyncWithMergeUrlParams({ bar: 'baz' }, 1, mergeUrlParams.mock.results[0].value);
    });
  });

  describe('with slot', () => {
    const slots = {
      default: '<button>Normal Slot</button>',
    };

    it('renders the default slot', () => {
      createComponent({ props: { query: null }, slots });

      expect(findButton().exists()).toBe(true);
    });
  });
});