summaryrefslogtreecommitdiff
path: root/spec/frontend/diffs/utils/discussions_spec.js
blob: 9a3d442d9431b23ea1fa082c4cf7b7631dbf887d (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
import { discussionIntersectionObserverHandlerFactory } from '~/diffs/utils/discussions';

describe('Diff Discussions Utils', () => {
  describe('discussionIntersectionObserverHandlerFactory', () => {
    it('creates a handler function', () => {
      expect(discussionIntersectionObserverHandlerFactory()).toBeInstanceOf(Function);
    });

    describe('intersection observer handler', () => {
      const functions = {
        setCurrentDiscussionId: jest.fn(),
        getPreviousUnresolvedDiscussionId: jest.fn().mockImplementation((id) => {
          return Number(id) - 1;
        }),
      };
      const defaultProcessableWrapper = {
        entry: {
          time: 0,
          isIntersecting: true,
          rootBounds: {
            bottom: 0,
          },
          boundingClientRect: {
            top: 0,
          },
        },
        currentDiscussion: {
          id: 1,
        },
        isFirstUnresolved: false,
        isDiffsPage: true,
      };
      let handler;
      let getMock;
      let setMock;

      beforeEach(() => {
        functions.setCurrentDiscussionId.mockClear();
        functions.getPreviousUnresolvedDiscussionId.mockClear();

        defaultProcessableWrapper.functions = functions;

        setMock = functions.setCurrentDiscussionId.mock;
        getMock = functions.getPreviousUnresolvedDiscussionId.mock;
        handler = discussionIntersectionObserverHandlerFactory();
      });

      it('debounces multiple simultaneous requests into one queue', () => {
        handler(defaultProcessableWrapper);
        handler(defaultProcessableWrapper);
        handler(defaultProcessableWrapper);
        handler(defaultProcessableWrapper);

        expect(setTimeout).toHaveBeenCalledTimes(4);
        expect(clearTimeout).toHaveBeenCalledTimes(3);

        // By only advancing to one timer, we ensure it's all being batched into one queue
        jest.advanceTimersToNextTimer();

        expect(functions.setCurrentDiscussionId).toHaveBeenCalledTimes(4);
      });

      it('properly processes, sorts and executes the correct actions for a set of observed intersections', () => {
        handler(defaultProcessableWrapper);
        handler({
          // This observation is here to be filtered out because it's a scrollDown
          ...defaultProcessableWrapper,
          entry: {
            ...defaultProcessableWrapper.entry,
            isIntersecting: false,
            boundingClientRect: { top: 10 },
            rootBounds: { bottom: 100 },
          },
        });
        handler({
          ...defaultProcessableWrapper,
          entry: {
            ...defaultProcessableWrapper.entry,
            time: 101,
            isIntersecting: false,
            rootBounds: { bottom: -100 },
          },
          currentDiscussion: { id: 20 },
        });
        handler({
          ...defaultProcessableWrapper,
          entry: {
            ...defaultProcessableWrapper.entry,
            time: 100,
            isIntersecting: false,
            boundingClientRect: { top: 100 },
          },
          currentDiscussion: { id: 30 },
          isDiffsPage: false,
        });
        handler({
          ...defaultProcessableWrapper,
          isFirstUnresolved: true,
          entry: {
            ...defaultProcessableWrapper.entry,
            time: 100,
            isIntersecting: false,
            boundingClientRect: { top: 200 },
          },
        });

        jest.advanceTimersToNextTimer();

        expect(setMock.calls.length).toBe(4);
        expect(setMock.calls[0]).toEqual([1]);
        expect(setMock.calls[1]).toEqual([29]);
        expect(setMock.calls[2]).toEqual([null]);
        expect(setMock.calls[3]).toEqual([19]);

        expect(getMock.calls.length).toBe(2);
        expect(getMock.calls[0]).toEqual([30, false]);
        expect(getMock.calls[1]).toEqual([20, true]);

        [
          setMock.invocationCallOrder[0],
          getMock.invocationCallOrder[0],
          setMock.invocationCallOrder[1],
          setMock.invocationCallOrder[2],
          getMock.invocationCallOrder[1],
          setMock.invocationCallOrder[3],
        ].forEach((order, idx, list) => {
          // Compare each invocation sequence to the one before it (except the first one)
          expect(list[idx - 1] || -1).toBeLessThan(order);
        });
      });
    });
  });
});