summaryrefslogtreecommitdiff
path: root/spec/frontend/protected_branches/protected_branch_edit_spec.js
blob: b4029d94980e0f49c23d2cd5623e6a57b0c0906d (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
146
147
148
149
150
151
152
153
154
155
156
157
158
159
import MockAdapter from 'axios-mock-adapter';
import $ from 'jquery';
import { setHTMLFixture, resetHTMLFixture } from 'helpers/fixtures';
import { TEST_HOST } from 'helpers/test_constants';
import { createAlert } from '~/flash';
import axios from '~/lib/utils/axios_utils';
import { HTTP_STATUS_INTERNAL_SERVER_ERROR, HTTP_STATUS_OK } from '~/lib/utils/http_status';
import ProtectedBranchEdit from '~/protected_branches/protected_branch_edit';

jest.mock('~/flash');

const TEST_URL = `${TEST_HOST}/url`;
const FORCE_PUSH_TOGGLE_TESTID = 'force-push-toggle';
const CODE_OWNER_TOGGLE_TESTID = 'code-owner-toggle';
const IS_CHECKED_CLASS = 'is-checked';
const IS_DISABLED_CLASS = 'is-disabled';
const IS_LOADING_SELECTOR = '.toggle-loading';

describe('ProtectedBranchEdit', () => {
  let mock;

  beforeEach(() => {
    jest.spyOn(ProtectedBranchEdit.prototype, 'buildDropdowns').mockImplementation();

    mock = new MockAdapter(axios);
  });

  const findForcePushToggle = () =>
    document.querySelector(`div[data-testid="${FORCE_PUSH_TOGGLE_TESTID}"] button`);
  const findCodeOwnerToggle = () =>
    document.querySelector(`div[data-testid="${CODE_OWNER_TOGGLE_TESTID}"] button`);

  const create = ({
    forcePushToggleChecked = false,
    codeOwnerToggleChecked = false,
    hasLicense = true,
  } = {}) => {
    setHTMLFixture(`<div id="wrap" data-url="${TEST_URL}">
      <span
        class="js-force-push-toggle"
        data-label="Toggle allowed to force push"
        data-is-checked="${forcePushToggleChecked}"
        data-testid="${FORCE_PUSH_TOGGLE_TESTID}"></span>
      <span
        class="js-code-owner-toggle"
        data-label="Toggle code owner approval"
        data-is-checked="${codeOwnerToggleChecked}"
        data-testid="${CODE_OWNER_TOGGLE_TESTID}"></span>
    </div>`);

    return new ProtectedBranchEdit({ $wrap: $('#wrap'), hasLicense });
  };

  afterEach(() => {
    mock.restore();
    resetHTMLFixture();
  });

  describe('when license supports code owner approvals', () => {
    beforeEach(() => {
      create();
    });

    it('instantiates the code owner toggle', () => {
      expect(findCodeOwnerToggle()).not.toBe(null);
    });
  });

  describe('when license does not support code owner approvals', () => {
    beforeEach(() => {
      create({ hasLicense: false });
    });

    it('does not instantiate the code owner toggle', () => {
      expect(findCodeOwnerToggle()).toBe(null);
    });
  });

  describe('when toggles are not available in the DOM on page load', () => {
    beforeEach(() => {
      create({ hasLicense: true });
      setHTMLFixture('');
    });

    afterEach(() => {
      resetHTMLFixture();
    });

    it('does not instantiate the force push toggle', () => {
      expect(findForcePushToggle()).toBe(null);
    });

    it('does not instantiate the code owner toggle', () => {
      expect(findCodeOwnerToggle()).toBe(null);
    });
  });

  describe.each`
    description     | checkedOption               | patchParam                        | finder
    ${'force push'} | ${'forcePushToggleChecked'} | ${'allow_force_push'}             | ${findForcePushToggle}
    ${'code owner'} | ${'codeOwnerToggleChecked'} | ${'code_owner_approval_required'} | ${findCodeOwnerToggle}
  `('when unchecked $description toggle button', ({ checkedOption, patchParam, finder }) => {
    let toggle;

    beforeEach(() => {
      create({ [checkedOption]: false });

      toggle = finder();
    });

    it('is not changed', () => {
      expect(toggle).not.toHaveClass(IS_CHECKED_CLASS);
      expect(toggle.querySelector(IS_LOADING_SELECTOR)).toBe(null);
      expect(toggle).not.toHaveClass(IS_DISABLED_CLASS);
    });

    describe('when clicked', () => {
      beforeEach(async () => {
        mock
          .onPatch(TEST_URL, { protected_branch: { [patchParam]: true } })
          .replyOnce(HTTP_STATUS_OK, {});
      });

      it('checks and disables button', async () => {
        await toggle.click();

        expect(toggle).toHaveClass(IS_CHECKED_CLASS);
        expect(toggle.querySelector(IS_LOADING_SELECTOR)).not.toBe(null);
        expect(toggle).toHaveClass(IS_DISABLED_CLASS);
      });

      it('sends update to BE', async () => {
        await toggle.click();

        await axios.waitForAll();

        // Args are asserted in the `.onPatch` call
        expect(mock.history.patch).toHaveLength(1);

        expect(toggle).not.toHaveClass(IS_DISABLED_CLASS);
        expect(toggle.querySelector(IS_LOADING_SELECTOR)).toBe(null);
        expect(createAlert).not.toHaveBeenCalled();
      });
    });

    describe('when clicked and BE error', () => {
      beforeEach(() => {
        mock.onPatch(TEST_URL).replyOnce(HTTP_STATUS_INTERNAL_SERVER_ERROR);
        toggle.click();
      });

      it('flashes error', async () => {
        await axios.waitForAll();

        expect(createAlert).toHaveBeenCalled();
      });
    });
  });
});