tests/action-handler.test.ts   A
last analyzed

Complexity

Total Complexity 1
Complexity/F 0

Size

Lines of Code 742
Function Count 0

Duplication

Duplicated Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
wmc 1
eloc 560
mnd 1
bc 1
fnc 0
dl 0
loc 742
rs 10
bpm 0
cpm 0
noi 0
c 0
b 0
f 0
1
// @ts-ignore: mock
2
import PollCard, {mockCreateCardWithId} from '../src/cards/PollCard';
3
// @ts-ignore: mock
4
import ClosePollFormCard, {mockCreateClosePollFormCard} from '../src/cards/ClosePollFormCard';
5
// @ts-ignore: mock
6
import ScheduleClosePollFormCard, {mockScheduleCreateClosePollFormCard} from '../src/cards/ScheduleClosePollFormCard';
7
8
import ActionHandler from '../src/handlers/ActionHandler';
9
// @ts-ignore: dummy test
10
import dummyAddOptionForm from './json/add_option_form.json';
11
import {mockCreate, mockGoogleAuth, mockUpdate} from './mocks';
12
import {createDialogActionResponse, createStatusActionResponse} from '../src/helpers/response';
13
import NewPollFormCard from '../src/cards/NewPollFormCard';
14
import {chat_v1 as chatV1} from '@googleapis/chat';
15
import {ClosableType, PollForm} from '../src/helpers/interfaces';
16
import {PROHIBITED_ICON_URL} from '../src/config/default';
17
import MessageDialogCard from '../src/cards/MessageDialogCard';
18
import {dummyLocalTimezone} from './dummy';
19
import {DEFAULT_LOCALE_TIMEZONE} from '../src/helpers/time';
20
import PollDialogCard, {mockCreatePollDialogCard} from '../src/cards/PollDialogCard';
21
22
jest.mock('../src/cards/PollCard');
23
jest.mock('../src/cards/PollDialogCard');
24
jest.mock('../src/cards/ClosePollFormCard');
25
jest.mock('../src/cards/ScheduleClosePollFormCard');
26
27
jest.mock('@googleapis/chat', () => {
28
  return {
29
    auth: {
30
      GoogleAuth: jest.fn(() => mockGoogleAuth),
31
    },
32
    chat: jest.fn().mockImplementation(() => {
33
      return {
34
        spaces: {
35
          messages: {
36
            create: mockCreate,
37
            update: mockUpdate,
38
          },
39
        },
40
      };
41
    }),
42
  };
43
});
44
jest.mock('@google-cloud/tasks', () => {
45
  return {
46
    CloudTasksClient: jest.fn(() => {
47
      return {
48
        createTask: jest.fn().mockResolvedValue([{name: 'testEmail'}]),
49
        queuePath: jest.fn().mockResolvedValue({
50
          email: 'testEmail',
51
        }),
52
      };
53
    }),
54
  };
55
});
56
57
it('should add a new option to the poll state and return an "OK" status message', async () => {
58
  // Mock event object
59
  const event = {
60
    user: {
61
      displayName: 'John Doe',
62
    },
63
    common: {
64
      formInputs: {
65
        value: {
66
          stringInputs: {
67
            value: ['Option 1'],
68
          },
69
        },
70
      },
71
    },
72
    message: {
73
      name: 'messageName',
74
    },
75
  };
76
77
  // Mock getEventPollState function
78
  const getEventPollStateMock = jest.fn().mockReturnValue({choices: [], choiceCreator: {}});
79
80
  // Create instance of ActionHandler
81
  const actionHandler = new ActionHandler(event);
82
83
  // Mock getEventPollState method
84
  actionHandler.getEventPollState = getEventPollStateMock;
85
86
  // Call saveOption method
87
  const result = await actionHandler.saveOption();
88
89
  expect(getEventPollStateMock).toHaveBeenCalled();
90
  expect(mockUpdate).toHaveBeenCalledWith({name: 'messageName', requestBody: {cardsV2: []}, updateMask: 'cardsV2'});
91
  expect(result).toEqual(createStatusActionResponse('Option is added'));
92
93
  const actionHandler2 = new ActionHandler(event);
94
95
  actionHandler2.getEventPollState = getEventPollStateMock;
96
  mockUpdate.mockResolvedValue({status: 400});
97
  const result2 = await actionHandler2.saveOption();
98
  expect(result2).toEqual(createStatusActionResponse('Failed to add option.', 'UNKNOWN'));
99
});
100
101
describe('process', () => {
102
  it('should return a message with a poll card when the action is "start_poll"', async () => {
103
    // Mock the startPoll function
104
    const startPollMock = jest.fn().mockReturnValue({});
105
106
    // Create an instance of ActionHandler
107
    const actionHandler = new ActionHandler({common: {invokedFunction: 'start_poll'}});
108
109
    // Mock the startPoll function in the ActionHandler instance
110
    actionHandler.startPoll = startPollMock;
111
112
    // Call the process method
113
    await actionHandler.process();
114
115
    // Expect the startPoll  to be called
116
    expect(startPollMock).toHaveBeenCalled();
117
  });
118
119
  it('should return a message with an updated poll card when the action is "vote"', async () => {
120
    // Mock the recordVote function
121
    const recordVoteMock = jest.fn().mockReturnValue({});
122
123
    // Create an instance of ActionHandler
124
    const actionHandler = new ActionHandler({common: {invokedFunction: 'vote'}});
125
126
    // Mock the recordVote function in the ActionHandler instance
127
    actionHandler.recordVote = recordVoteMock;
128
129
    // Call the process method
130
    await actionHandler.process();
131
132
    // Expect the recordVote function to be called
133
    expect(recordVoteMock).toHaveBeenCalled();
134
  });
135
136
  it('should return a dialog with an add option form when the action is "add_option_form"', async () => {
137
    // Mock the addOptionForm function
138
    const addOptionFormMock = jest.fn().mockReturnValue({});
139
140
    // Create an instance of ActionHandler
141
    const actionHandler = new ActionHandler({common: {invokedFunction: 'add_option_form'}});
142
143
    // Mock the addOptionForm function in the ActionHandler instance
144
    actionHandler.addOptionForm = addOptionFormMock;
145
146
    // Call the process method
147
    await actionHandler.process();
148
149
    // Expect the addOptionForm function to be called
150
    expect(addOptionFormMock).toHaveBeenCalled();
151
  });
152
  it('should return a dialog with an add option form when the action is "add_option_form"', async () => {
153
    // Mock the addOptionForm function
154
    const addOptionFormMock = jest.fn().mockReturnValue({});
155
156
    // Create an instance of ActionHandler
157
    const actionHandler = new ActionHandler({common: {invokedFunction: 'add_option_form'}});
158
159
    // Mock the addOptionForm function in the ActionHandler instance
160
    actionHandler.addOptionForm = addOptionFormMock;
161
162
    // Call the process method
163
    await actionHandler.process();
164
165
    // Expect the addOptionForm function to be called
166
    expect(addOptionFormMock).toHaveBeenCalled();
167
  });
168
169
  it('should create a dialog with AddOptionFormCard and return it as an actionResponse', () => {
170
    // Arrange
171
    const card = {
172
      sections: [
173
        {
174
          widgets: [
175
            {
176
              decoratedText: {
177
                button: {
178
                  onClick: {
179
                    action: {
180
                      parameters: [
181
                        {
182
                          value: '{"topic":"Who is the most handsome AI?", "choices": []}',
183
                        },
184
                      ],
185
                    },
186
                  },
187
                },
188
              },
189
            },
190
          ],
191
        },
192
      ],
193
    };
194
    const cardWithId: chatV1.Schema$CardWithId = {
195
      cardId: 'cardId',
196
      card,
197
    };
198
    const event = {
199
      message: {
200
        cardsV2: [cardWithId],
201
      },
202
    };
203
    const expectedDialog = {
204
      body: dummyAddOptionForm,
205
    };
206
    const actionHandler = new ActionHandler(event);
207
    actionHandler.getEventPollState = jest.fn().
208
      mockReturnValue({'topic': 'Who is the most handsome AI?', 'choices': []});
209
    // Act
210
    const result = actionHandler.addOptionForm();
211
212
    // Assert
213
    expect(result).toEqual({
214
      actionResponse: {
215
        type: 'DIALOG',
216
        dialogAction: {
217
          dialog: expectedDialog,
218
        },
219
      },
220
    });
221
  });
222
223
  // Tests that the 'add_option' action returns a message with an updated poll card
224
  it('should return a message with an updated poll card when the action is "add_option"', async () => {
225
    // Mock the saveOption function
226
    const saveOptionMock = jest.fn().mockReturnValue({});
227
228
    // Create an instance of ActionHandler
229
    const actionHandler = new ActionHandler({common: {invokedFunction: 'add_option'}});
230
231
    // Mock the saveOption function in the ActionHandler instance
232
    actionHandler.saveOption = saveOptionMock;
233
234
    // Call the process method
235
    await actionHandler.process();
236
237
    // Expect the saveOption function to be called
238
    expect(saveOptionMock).toHaveBeenCalled();
239
  });
240
241
  it('should return a message with an updated poll card when the action is "close_poll_form"', async () => {
242
    // Mock the closePollForm function
243
    const closePollFormMock = jest.fn().mockReturnValue({});
244
245
    // Create an instance of ActionHandler
246
    const actionHandler = new ActionHandler({common: {invokedFunction: 'close_poll_form'}});
247
248
    // Mock the closePollForm function in the ActionHandler instance
249
    actionHandler.closePollForm = closePollFormMock;
250
251
    // Call the process method
252
    await actionHandler.process();
253
254
    // Expect the saveOption function to be called
255
    expect(closePollFormMock).toHaveBeenCalled();
256
  });
257
258
  it('other actions"', async () => {
259
    const switchVoteMock = jest.fn().mockReturnValue({});
260
    const voteFormMock = jest.fn().mockReturnValue({});
261
262
    const actionHandler = new ActionHandler({common: {invokedFunction: 'switch_vote'}});
263
    actionHandler.switchVote = switchVoteMock;
264
    await actionHandler.process();
265
266
    const actionHandler2 = new ActionHandler({common: {invokedFunction: 'vote_form'}});
267
    actionHandler2.voteForm = voteFormMock;
268
    await actionHandler2.process();
269
270
271
    expect(switchVoteMock).toHaveBeenCalled();
272
    expect(voteFormMock).toHaveBeenCalled();
273
  });
274
275
  // Tests that the 'unknown' action returns a message with an updated poll card
276
  it('should return a message with an updated poll card when the action is "add_option"', async () => {
277
    // Create an instance of ActionHandler
278
    const actionHandler = new ActionHandler({common: {invokedFunction: 'unknown'}});
279
280
    // Call the process method
281
    const result = await actionHandler.process();
282
283
    // Expect the saveOption function to be called
284
    expect(result).toEqual(createStatusActionResponse('Unknown action!', 'UNKNOWN'));
285
  });
286
287
  it('should rebuild poll form with inputted data when new_poll_on_change invoked', async () => {
288
    const event = {
289
      common: {
290
        invokedFunction: 'new_poll_on_change',
291
        formInputs: {
292
          topic: {stringInputs: {value: ['Yay or Nay']}},
293
          allow_add_option: {stringInputs: {value: ['0']}},
294
          type: {stringInputs: {value: ['2']}},
295
          option0: {stringInputs: {value: ['Yay']}},
296
          option1: {stringInputs: {value: ['Nae']}},
297
        },
298
      },
299
      user: {displayName: 'User'},
300
      space: {name: 'Space'},
301
    };
302
    const actionHandler = new ActionHandler(event);
303
    const result = await actionHandler.process();
304
    const expectedConfig: PollForm = {topic: 'Yay or Nay', choices: ['Yay', 'Nae'], optionable: false, type: 2};
305
    const expectedCard = new NewPollFormCard(expectedConfig, dummyLocalTimezone).create();
306
    const expectedResponse = createDialogActionResponse(expectedCard);
307
    expect(result).toEqual(expectedResponse);
308
  });
309
});
310
311
describe('startPoll', () => {
312
  // Tests that a valid form is submitted and a poll card is created and displayed in the space
313
  it('should create and display poll card when valid form is submitted', async () => {
314
    const event = {
315
      common: {
316
        invokedFunction: 'start_poll',
317
        formInputs: {
318
          topic: {stringInputs: {value: ['Topic']}},
319
          allow_add_option: {stringInputs: {value: ['0']}},
320
          type: {stringInputs: {value: ['0']}},
321
          option0: {stringInputs: {value: ['Yay']}},
322
          option1: {stringInputs: {value: ['Nae']}},
323
          option2: {stringInputs: {value: ['']}},
324
          option3: {stringInputs: {value: ['']}},
325
          option4: {stringInputs: {value: ['']}},
326
          option5: {stringInputs: {value: ['No Way']}},
327
          is_autoclose: {stringInputs: {value: ['1']}},
328
          close_schedule_time: {dateTimeInput: {msSinceEpoch: Date.now().toString()}},
329
        },
330
        timeZone: {'id': 'America/New_York'},
331
      },
332
      user: {displayName: 'User'},
333
      space: {name: 'Space'},
334
    };
335
336
    const actionHandler = new ActionHandler(event);
337
338
    // will throw error because not all required environment variables are set
339
    await expect(async () => {
340
      await actionHandler.startPoll();
341
    }).rejects.toThrowError('Missing required environment variables');
342
    // duplicate test with another way for reference to test the error
343
    await actionHandler.startPoll().catch((error) => {
344
      expect(error).toEqual(new Error('Missing required environment variables'));
345
    });
346
347
    process.env.GCP_PROJECT = 'test-project';
348
    process.env.QUEUE_NAME = 'test-queue';
349
    process.env.FUNCTION_REGION = 'us-central1';
350
    const result = await actionHandler.startPoll();
351
352
    const pollCardMessage = new PollCard({
353
      topic: 'Topic',
354
      choiceCreator: undefined,
355
      author: event.user,
356
      choices: ['Yae', 'Nae', 'No Way'],
357
      votes: {'0': [], '1': []},
358
      anon: false,
359
      optionable: false,
360
    }, dummyLocalTimezone).createMessage();
361
362
    const request = {
363
      parent: event.space?.name,
364
      requestBody: pollCardMessage,
365
    };
366
367
    expect(result).toEqual(createStatusActionResponse('Poll started.', 'OK'));
368
    expect(mockCreate).toHaveBeenCalledWith(request);
369
370
    // when google API return invalid data, it should return an error message
371
    mockCreate.mockResolvedValue({status: 400, data: {}});
372
    const actionHandler2 = new ActionHandler(event);
373
    const result2 = await actionHandler2.startPoll();
374
    expect(result2).toEqual(createStatusActionResponse('Failed to start poll.', 'UNKNOWN'));
375
  });
376
377
  // Tests that an incomplete form is submitted and the form is rerendered
378
  it('should rerender form when incomplete form is submitted', async () => {
379
    // Mock event object
380
    const event = {
381
      common: {
382
        formInputs: {
383
          topic: {stringInputs: {value: ['']}},
384
          is_anonymous: {stringInputs: {value: ['1']}},
385
          allow_add_option: {stringInputs: {value: ['1']}},
386
          type: {stringInputs: {value: ['1']}},
387
          option0: {stringInputs: {value: ['Option 1']}},
388
        },
389
      },
390
    };
391
392
    const actionHandler = new ActionHandler(event);
393
394
    const result = await actionHandler.startPoll();
395
396
    expect(result).toEqual({
397
      actionResponse: {
398
        type: 'DIALOG',
399
        dialogAction: {
400
          dialog: {
401
            body: new NewPollFormCard({
402
              topic: '',
403
              choices: ['Option 1'],
404
              anon: true,
405
              type: 1,
406
            }, dummyLocalTimezone).create(),
407
          },
408
        },
409
      },
410
    });
411
  });
412
413
  // we should validate the input from the form
414
  it('should rerender form when the closed time less than now', async () => {
415
    // Mock event object
416
    const event = {
417
      common: {
418
        invokedFunction: 'start_poll',
419
        formInputs: {
420
          topic: {stringInputs: {value: ['Yay or Nae']}},
421
          allow_add_option: {stringInputs: {value: ['0']}},
422
          type: {stringInputs: {value: ['0']}},
423
          option0: {stringInputs: {value: ['Yay']}},
424
          option1: {stringInputs: {value: ['Nae']}},
425
          option2: {stringInputs: {value: ['']}},
426
          is_autoclose: {stringInputs: {value: ['1']}},
427
          close_schedule_time: {dateTimeInput: {msSinceEpoch: (Date.now() - 3600000).toString()}},
428
        },
429
        timeZone: {'id': 'Asia/Jakarta'},
430
      },
431
      user: {displayName: 'User'},
432
      space: {name: 'Space'},
433
    };
434
435
    const actionHandler = new ActionHandler(event);
436
437
    const result = await actionHandler.startPoll();
438
    expect(result.actionResponse.dialogAction.dialog.body).toBeDefined();
439
  });
440
});
441
describe('recordVote', () => {
442
  it('should throw an error if the index parameter is missing', () => {
443
    const event = {
444
      common: {
445
        parameters: {},
446
      },
447
    };
448
    const actionHandler = new ActionHandler(event);
449
450
    expect(() => actionHandler.recordVote()).toThrow('Index Out of Bounds');
451
    expect(() => actionHandler.getEventPollState()).toThrow('no valid card in the event');
452
    const event2 = {
453
      common: {
454
        parameters: {},
455
      },
456
      message: {
457
        thread: {
458
          'name': 'spaces/AAAAN0lf83o/threads/DJXfo5DXcTA',
459
        },
460
        cardsV2: [{cardId: 'card', card: {}}],
461
      },
462
    };
463
    const actionHandler2 = new ActionHandler(event2);
464
    expect(() => actionHandler2.getEventPollState()).toThrow('no valid state in the event');
465
  });
466
  it('should update an existing vote with a new vote', () => {
467
    const event = {
468
      common: {
469
        parameters: {
470
          index: '1',
471
          state: '{"votes": {"0": [{"uid": "userId", "name": "userName"}]}, "anon": false}',
472
        },
473
      },
474
      user: {
475
        name: 'userId2',
476
        displayName: 'userName2',
477
      },
478
      message: {
479
        thread: {
480
          'name': 'spaces/AAAAN0lf83o/threads/DJXfo5DXcTA',
481
        },
482
        cardsV2: [{cardId: 'card', card: {}}],
483
      },
484
    };
485
    const actionHandler = new ActionHandler(event);
486
    const response = actionHandler.recordVote();
487
488
    const expectedResponse = {
489
      thread: {
490
        'name': 'spaces/AAAAN0lf83o/threads/DJXfo5DXcTA',
491
      },
492
      actionResponse: {
493
        type: 'UPDATE_MESSAGE',
494
      },
495
      cardsV2: ['card'],
496
    };
497
    const expectedPollState = {
498
      votes: {
499
        '0': [{uid: 'userId', name: 'userName'}],
500
        '1': [{uid: 'userId2', name: 'userName2'}],
501
      }, anon: false,
502
    };
503
    expect(PollCard).toHaveBeenCalledWith(expectedPollState, DEFAULT_LOCALE_TIMEZONE);
504
    expect(mockCreateCardWithId).toHaveBeenCalled();
505
    expect(response).toEqual(expectedResponse);
506
    expect(actionHandler.getEventPollState()).toEqual({
507
      votes: {
508
        '0': [{uid: 'userId', name: 'userName'}],
509
      }, anon: false,
510
    });
511
  });
512
});
513
514
describe('closePoll', () => {
515
  it('should close the poll and return a status message with "Poll is closed" and status "OK"', async () => {
516
    const expectedResponse = {
517
      actionResponse: {
518
        type: 'DIALOG',
519
        dialogAction: {
520
          actionStatus: {
521
            statusCode: 'OK',
522
            userFacingMessage: 'Poll is closed',
523
          },
524
        },
525
      },
526
    };
527
    mockUpdate.mockResolvedValue({'status': 200, 'data': {}});
528
529
    const actionHandler = new ActionHandler({message: {name: 'messageName'}});
530
    actionHandler.getEventPollState = jest.fn().mockReturnValue({});
531
    const result = await actionHandler.closePoll();
532
533
    expect(result).toEqual(expectedResponse);
534
    expect(mockUpdate).toHaveBeenCalledWith({name: 'messageName', requestBody: {cardsV2: []}, updateMask: 'cardsV2'});
535
  });
536
537
  it('should return a status message with "Failed to close poll." when the call to "callMessageApi" fails',
538
    async () => {
539
      // Arrange
540
      const state = {
541
        closedTime: undefined,
542
      };
543
      const expectedResponse = {
544
        actionResponse: {
545
          type: 'DIALOG',
546
          dialogAction: {
547
            actionStatus: {
548
              statusCode: 'UNKNOWN',
549
              userFacingMessage: 'Failed to close poll.',
550
            },
551
          },
552
        },
553
      };
554
555
      mockUpdate.mockResolvedValue({'status': 400, 'data': {}});
556
557
      const actionHandler = new ActionHandler({message: {name: 'messageName'}});
558
      actionHandler.getEventPollState = jest.fn().mockReturnValue(state);
559
560
      const result = await actionHandler.closePoll();
561
562
      expect(result).toEqual(expectedResponse);
563
      expect(state.closedTime).toBeDefined();
564
565
      mockUpdate.mockResolvedValue(undefined);
566
      await actionHandler.closePoll().catch((error) => {
567
        expect(error).toEqual(new Error('Empty response'));
568
      });
569
    });
570
});
571
572
describe('closePollForm', () => {
573
  it('should allow the creator of the poll with CLOSEABLE_BY_CREATOR type to close the poll', () => {
574
    const state = {
575
      type: ClosableType.CLOSEABLE_BY_CREATOR,
576
      author: {name: 'creator'},
577
    };
578
    const event = {
579
      user: {name: 'creator'},
580
    };
581
    const actionHandler = new ActionHandler(event);
582
    actionHandler.getEventPollState = jest.fn().mockReturnValue(state);
583
    actionHandler.closePollForm();
584
585
    expect(ClosePollFormCard).toHaveBeenCalledWith(state, DEFAULT_LOCALE_TIMEZONE);
586
    expect(mockCreateClosePollFormCard).toHaveBeenCalled();
587
  });
588
  it('should disallow the creator of the poll with CLOSEABLE_BY_CREATOR type to close the poll', () => {
589
    const state = {
590
      type: ClosableType.CLOSEABLE_BY_CREATOR,
591
      author: {name: 'creator', displayName: 'creator user'},
592
    };
593
    const event = {
594
      user: {name: 'other user'},
595
    };
596
    const actionHandler = new ActionHandler(event);
597
    actionHandler.getEventPollState = jest.fn().mockReturnValue(state);
598
599
    const dialogConfig = {
600
      title: 'Sorry, you can not close this poll',
601
      message: `The poll setting restricts the ability to close the poll to only the creator(${state.author!.displayName}).`,
602
      imageUrl: PROHIBITED_ICON_URL,
603
    };
604
    const expectedResponse = createDialogActionResponse(new MessageDialogCard(dialogConfig).create());
605
    const result = actionHandler.closePollForm();
606
    expect(result).toEqual(expectedResponse);
607
  });
608
});
609
610
describe('scheduleClosePoll', () => {
611
  it('should return a message with an updated poll card when the action is "close_poll_form"', async () => {
612
    // Mock the closePollForm function
613
    const state = {
614
      type: ClosableType.CLOSEABLE_BY_CREATOR,
615
      author: {name: 'creator', displayName: 'creator test user'},
616
    };
617
    // Create an instance of ActionHandler
618
    const actionHandler = new ActionHandler({common: {invokedFunction: 'schedule_close_poll_form'}});
619
620
    actionHandler.getEventPollState = jest.fn().mockReturnValue(state);
621
622
    // Call the process method
623
    await actionHandler.process();
624
625
    // Expect the saveOption function to be called
626
    expect(ScheduleClosePollFormCard).toHaveBeenCalled();
627
    expect(mockScheduleCreateClosePollFormCard).toHaveBeenCalled();
628
    expect(actionHandler.getEventPollState).toHaveBeenCalled();
629
  });
630
631
  it('should return schedule close form card when the schedule input time is in the past', async () => {
632
    // Create an instance of ActionHandler
633
    const actionHandler = new ActionHandler({
634
      common: {
635
        invokedFunction: 'schedule_close_poll',
636
        formInputs: {
637
          close_schedule_time: {dateTimeInput: {msSinceEpoch: (Date.now() - 1000000).toString()}},
638
        },
639
      },
640
      message: {'name': 'anu'},
641
    });
642
    actionHandler.getEventPollState = jest.fn();
643
644
    process.env.GCP_PROJECT = 'test-project';
645
    process.env.QUEUE_NAME = 'test-queue';
646
    process.env.FUNCTION_REGION = 'us-central1';
647
    // Call the process method
648
    await actionHandler.process();
649
650
    expect(actionHandler.getEventPollState).not.toHaveBeenCalled();
651
    // since the schedule date is in the past, the form will show again
652
    expect(ScheduleClosePollFormCard).toHaveBeenCalled();
653
    expect(mockScheduleCreateClosePollFormCard).toHaveBeenCalled();
654
  });
655
});
656
it('should update message if close_schedule_time is correct', async () => {
657
  const ms = Date.now() + dummyLocalTimezone.offset + 1000000;
658
  // Create an instance of ActionHandler
659
  const actionHandler = new ActionHandler({
660
    common: {
661
      invokedFunction: 'schedule_close_poll',
662
      formInputs: {
663
        close_schedule_time: {dateTimeInput: {msSinceEpoch: ms.toString()}},
664
        auto_mention: {stringInputs: {value: ['1']}},
665
      },
666
      timeZone: {'id': dummyLocalTimezone.id, 'offset': dummyLocalTimezone.offset},
667
      userLocale: dummyLocalTimezone.locale,
668
    },
669
    message: {'name': 'anu'},
670
  });
671
672
  const state = {
673
    type: ClosableType.CLOSEABLE_BY_CREATOR,
674
    author: {name: 'creator', displayName: 'creator userzzzz'}, closedTime: undefined,
675
676
  };
677
  actionHandler.getEventPollState = jest.fn().mockReturnValue(state);
678
  mockUpdate.mockReturnValue(state);
679
  process.env.GCP_PROJECT = 'test-project';
680
  process.env.QUEUE_NAME = 'test-queue';
681
  process.env.FUNCTION_REGION = 'us-central1';
682
683
  // Call the process method
684
  await actionHandler.scheduleClosePoll();
685
686
  expect(actionHandler.getEventPollState).toHaveBeenCalled();
687
  expect(mockUpdate).toHaveBeenCalled();
688
  // since the schedule date is in the past, the form will show again
689
  expect(ScheduleClosePollFormCard).not.toHaveBeenCalled();
690
691
  expect(PollCard).toHaveBeenCalledWith(state, dummyLocalTimezone);
692
  expect(state.closedTime).toEqual(ms - dummyLocalTimezone.offset);
693
  // todo: create task toHaveBeenCalled
694
});
695
696
697
it('voteForm action', () => {
698
  const state = {
699
    type: ClosableType.CLOSEABLE_BY_CREATOR,
700
    author: {name: 'creator'},
701
    votes: {},
702
  };
703
  const event = {
704
    user: {name: '1123124124124', displayName: 'creator'},
705
    common: {
706
      parameters: {
707
        index: '1',
708
      },
709
      timeZone: {'id': dummyLocalTimezone.id, 'offset': dummyLocalTimezone.offset},
710
      userLocale: dummyLocalTimezone.locale,
711
    },
712
    message: {
713
      thread: {
714
        'name': 'spaces/AAAAN0lf83o/threads/DJXfo5DXcTA',
715
      },
716
      cardsV2: [{cardId: 'card', card: {}}],
717
    },
718
  };
719
  const actionHandler = new ActionHandler(event);
720
  actionHandler.getEventPollState = jest.fn().mockReturnValue(state);
721
  // Act
722
  actionHandler.voteForm();
723
  expect(PollCard).toHaveBeenCalledWith(state, dummyLocalTimezone);
724
  expect(PollDialogCard).toHaveBeenCalledWith(state, dummyLocalTimezone, {name: 'creator', uid: '1123124124124'});
725
  expect(mockCreatePollDialogCard).toHaveBeenCalled();
726
});
727
728
729
it('switchVote action', async () => {
730
  const event = {
731
    common: {
732
      parameters: {
733
      },
734
    },
735
  };
736
  const actionHandler = new ActionHandler(event);
737
738
  await expect(async () => {
739
    await actionHandler.voteForm();
740
  }).rejects.toThrowError('Index Out of Bounds');
741
});
742