1# --
2# Copyright (C) 2001-2020 OTRS AG, https://otrs.com/
3# --
4# This software comes with ABSOLUTELY NO WARRANTY. For details, see
5# the enclosed file COPYING for license information (GPL). If you
6# did not receive this file, see https://www.gnu.org/licenses/gpl-3.0.txt.
7# --
8
9use strict;
10use warnings;
11use utf8;
12
13use vars (qw($Self));
14
15$Kernel::OM->ObjectParamAdd(
16    'Kernel::System::UnitTest::Helper' => {
17        DisableAsyncCalls => 1,
18    },
19);
20
21my $Selenium = $Kernel::OM->Get('Kernel::System::UnitTest::Selenium');
22
23my $ElementReadOnly = sub {
24    my (%Param) = @_;
25
26    # Value is optional parameter.
27    for my $Needed (qw(UnitTestObject Element)) {
28        if ( !$Param{$Needed} ) {
29            $Kernel::OM->Get('Kernel::System::Log')->Log(
30                Priority => 'error',
31                Message  => "Need $Needed!",
32            );
33            return;
34        }
35    }
36
37    $Selenium->WaitFor( JavaScript => "return typeof(\$) === 'function' && \$('#$Param{Element}').length" );
38
39    $Param{UnitTestObject}->Is(
40        $Selenium->execute_script(
41            "return \$('#$Param{Element}.ReadOnlyValue').length;"
42        ),
43        $Param{Value},
44        "$Param{Element} read only ($Param{Value})",
45    );
46};
47
48my $ElementExists = sub {
49    my (%Param) = @_;
50
51    # Value is optional parameter.
52    for my $Needed (qw(UnitTestObject Element)) {
53        if ( !$Param{$Needed} ) {
54            $Kernel::OM->Get('Kernel::System::Log')->Log(
55                Priority => 'error',
56                Message  => "Need $Needed!",
57            );
58            return;
59        }
60    }
61
62    my $Length = $Selenium->execute_script(
63        "return \$('#$Param{Element}').length;"
64    );
65
66    if ( $Param{Value} ) {
67        $Param{UnitTestObject}->True(
68            $Length,
69            "$Param{Element} exists",
70        );
71    }
72    else {
73        $Param{UnitTestObject}->False(
74            $Length,
75            "$Param{Element} not exists",
76        );
77    }
78};
79
80$Selenium->RunTest(
81    sub {
82        my $Helper = $Kernel::OM->Get('Kernel::System::UnitTest::Helper');
83
84        my $GroupObject    = $Kernel::OM->Get('Kernel::System::Group');
85        my $CalendarObject = $Kernel::OM->Get('Kernel::System::Calendar');
86        my $TicketObject   = $Kernel::OM->Get('Kernel::System::Ticket');
87
88        my $RandomID = $Helper->GetRandomID();
89
90        # Create test group.
91        my $GroupName = "test-calendar-group-$RandomID";
92        my $GroupID   = $GroupObject->GroupAdd(
93            Name    => $GroupName,
94            ValidID => 1,
95            UserID  => 1,
96        );
97
98        # Create test group.
99        my $GroupName2 = "test-calendar-group2-$RandomID";
100        my $GroupID2   = $GroupObject->GroupAdd(
101            Name    => $GroupName2,
102            ValidID => 1,
103            UserID  => 1,
104        );
105
106        # Change resolution (desktop mode).
107        $Selenium->set_window_size( 768, 1050 );
108
109        # Create test user.
110        my $Language      = 'en';
111        my $TestUserLogin = $Helper->TestUserCreate(
112            Groups   => [ 'users', $GroupName ],
113            Language => $Language,
114        ) || die 'Did not get test user';
115
116        # Get UserID.
117        my $UserID = $Kernel::OM->Get('Kernel::System::User')->UserLookup(
118            UserLogin => $TestUserLogin,
119        );
120
121        my $TestUserLogin2 = $Helper->TestUserCreate(
122            Groups   => [ 'users', $GroupName2 ],
123            Language => $Language,
124        ) || die 'Did not get test user';
125
126        # Get UserID.
127        my $UserID2 = $Kernel::OM->Get('Kernel::System::User')->UserLookup(
128            UserLogin => $TestUserLogin2,
129        );
130
131        # Create test customer user.
132        my $TestCustomerUserLogin = $Helper->TestCustomerUserCreate()
133            || die 'Did not get test customer user';
134
135        # Create a few test calendars.
136        my %Calendar1 = $CalendarObject->CalendarCreate(
137            CalendarName => "Calendar1 $RandomID",
138            Color        => '#3A87AD',
139            GroupID      => $GroupID,
140            UserID       => $UserID,
141            ValidID      => 1,
142        );
143        $Self->True(
144            $Calendar1{CalendarID},
145            "Calendar1 $RandomID created successful.",
146        );
147
148        my %Calendar2 = $CalendarObject->CalendarCreate(
149            CalendarName => "Calendar2 $RandomID",
150            Color        => '#EC9073',
151            GroupID      => $GroupID,
152            UserID       => $UserID,
153            ValidID      => 1,
154        );
155        $Self->True(
156            $Calendar2{CalendarID},
157            "Calendar2 $RandomID created successful.",
158        );
159
160        my %Calendar3 = $CalendarObject->CalendarCreate(
161            CalendarName => "Calendar3 $RandomID",
162            Color        => '#6BAD54',
163            GroupID      => $GroupID,
164            UserID       => $UserID,
165            ValidID      => 1,
166        );
167        $Self->True(
168            $Calendar3{CalendarID},
169            "Calendar3 $RandomID created successful.",
170        );
171
172        my %Calendar4 = $CalendarObject->CalendarCreate(
173            CalendarName => "Calendar4 $RandomID",
174            Color        => '#78A7FC',
175            GroupID      => $GroupID2,
176            UserID       => $UserID2,
177            ValidID      => 1,
178        );
179        $Self->True(
180            $Calendar4{CalendarID},
181            "Calendar4 $RandomID created successful.",
182        );
183
184        # Create a test ticket.
185        my $TicketID = $TicketObject->TicketCreate(
186            Title        => "Link Ticket $RandomID",
187            Queue        => 'Raw',
188            Lock         => 'unlock',
189            Priority     => '3 normal',
190            State        => 'open',
191            CustomerNo   => '123465',
192            CustomerUser => 'customer@example.com',
193            OwnerID      => $UserID,
194            UserID       => $UserID,
195        );
196        $Self->True(
197            $TicketID,
198            "TicketCreate() - $TicketID",
199        );
200        my $TicketNumber = $TicketObject->TicketNumberLookup(
201            TicketID => $TicketID,
202            UserID   => $UserID,
203        );
204        $Self->True(
205            $TicketNumber,
206            "TicketNumberLookup() - $TicketNumber",
207        );
208
209        $Selenium->Login(
210            Type     => 'Agent',
211            User     => $TestUserLogin,
212            Password => $TestUserLogin,
213        );
214
215        my $ScriptAlias = $Kernel::OM->Get('Kernel::Config')->Get('ScriptAlias');
216
217        # Go to calendar overview page.
218        $Selenium->VerifiedGet("${ScriptAlias}index.pl?Action=AgentAppointmentCalendarOverview");
219        $Selenium->WaitFor(
220            JavaScript =>
221                'return typeof($) === "function" && !$(".CalendarWidget.Loading").length && $(".fc-toolbar .fc-prev-button").length;'
222        );
223
224        # Go to previous week.
225        $Selenium->find_element( '.fc-toolbar .fc-prev-button', 'css' )->click();
226        $Selenium->WaitFor(
227            JavaScript =>
228                'return typeof($) === "function" && !$(".CalendarWidget.Loading").length && $(".CalendarSwitch:visible").length == 3;'
229        );
230
231        # Verify all three calendars are visible.
232        $Self->Is(
233            $Selenium->execute_script(
234                "return \$('.CalendarSwitch:visible').length;"
235            ),
236            3,
237            'All three calendars visible',
238        );
239
240        # Verify copy-to-clipboard link.
241        my $URL = $Selenium->execute_script("return \$('.CopyToClipboard').attr('data-clipboard-text');");
242
243        $Self->True(
244            $URL,
245            'CopyToClipboard URL present'
246        );
247
248        # URL should not contain OTRS specific URL delimiter of semicolon (;).
249        #   For better compatibility, use standard ampersand (&) instead.
250        #   Please see bug#12667 for more information.
251        $Self->False(
252            ( $URL =~ /[;]/ ) ? 1 : 0,
253            'CopyToClipboard URL does not contain forbidden characters'
254        );
255
256        # Click on the timeline view for an appointment dialog.
257        $Selenium->find_element( '.fc-timelineWeek-view .fc-slats td.fc-widget-content:nth-child(5)', 'css' )->click();
258        $Selenium->WaitFor( JavaScript => "return typeof(\$) === 'function' && \$('#Title').length;" );
259
260        # Elements that are not allowed in dialog.
261        for my $Element (qw(EditFormDelete EditFormCopy)) {
262            $ElementExists->(
263                UnitTestObject => $Self,
264                Element        => $Element,
265                Value          => 0,
266            );
267        }
268
269        # Enter some data.
270        $Selenium->find_element( "#Title",    'css' )->send_keys('Appointment 1');
271        $Selenium->find_element( "#Location", 'css' )->send_keys('Straubing');
272        $Selenium->InputFieldValueSet(
273            Element => '#CalendarID',
274            Value   => $Calendar1{CalendarID},
275        );
276        $Selenium->find_element( '#EndHour',     'css' )->send_keys('18');
277        $Selenium->find_element( '.PluginField', 'css' )->send_keys($TicketNumber);
278        $Selenium->WaitFor( JavaScript => 'return typeof($) === "function" && $("li.ui-menu-item:visible").length;' );
279        $Selenium->execute_script("\$('li.ui-menu-item:contains($TicketNumber)').click();");
280
281        $Selenium->WaitFor(
282            JavaScript =>
283                "return typeof(\$) === 'function' && \$('.PluginContainer div a[target=\"_blank\"]').length;"
284        );
285
286        # Verify correct ticket is listed.
287        $Self->Is(
288            $Selenium->execute_script(
289                "return \$('.PluginContainer div a[target=\"_blank\"]').text();"
290            ),
291            "$TicketNumber Link Ticket $RandomID",
292            'Link ticket visible',
293        );
294
295        # Check location link contains correct value.
296        my $LocationLinkURL = $Selenium->execute_script("return \$('.LocationLink').attr('href');");
297        $Self->True(
298            $LocationLinkURL =~ /Straubing$/,
299            'Location link contains correct value',
300        );
301
302        # Click on Save.
303        $Selenium->find_element( '#EditFormSubmit', 'css' )->click();
304        $Selenium->WaitFor(
305            JavaScript =>
306                'return typeof($) === "function" && !$(".Dialog:visible").length && !$(".CalendarWidget.Loading").length;'
307        );
308
309        # Wait until all AJAX calls finished.
310        $Selenium->WaitFor(
311            JavaScript => "return \$.active == 0 && \$('.fc-timeline-event .fc-title').text() == 'Appointment 1';"
312        );
313
314        # Verify first appointment is visible.
315        $Self->Is(
316            $Selenium->execute_script(
317                "return \$('.fc-timeline-event .fc-title').text();"
318            ),
319            'Appointment 1',
320            'First appointment visible',
321        );
322
323        # Go to the ticket zoom screen.
324        $Selenium->VerifiedGet("${ScriptAlias}index.pl?Action=AgentTicketZoom;TicketID=${TicketID}");
325
326        # Find link to the appointment on page.
327        $Selenium->WaitFor( JavaScript => "return \$('a.LinkObjectLink').length;" );
328        $Selenium->execute_script(
329            "\$('.LinkObjectLink')[0].scrollIntoView(true);",
330        );
331        $Selenium->execute_script('$("a.LinkObjectLink")[0].click();');
332        $Selenium->WaitFor(
333            JavaScript => "return typeof(\$) === 'function' && \$('#Title').length && \$('#CalendarID').length;"
334        );
335
336        $Self->Is(
337            $Selenium->execute_script(
338                "return \$('#Title').val();"
339            ),
340            'Appointment 1',
341            'Title matches',
342        );
343        $Self->Is(
344            $Selenium->execute_script(
345                "return \$('#CalendarID').val();"
346            ),
347            $Calendar1{CalendarID},
348            'Calendar matches',
349        );
350        $Self->Is(
351            $Selenium->execute_script(
352                "return \$('.PluginContainer div a[href*=\"Action=AgentTicketZoom;TicketID=$TicketID\"]').text();"
353            ),
354            "$TicketNumber Link Ticket $RandomID",
355            'Link ticket matches',
356        );
357
358        # Check location link contains correct value.
359        $LocationLinkURL = $Selenium->execute_script("return \$('.LocationLink').attr('href');");
360        $Self->True(
361            $LocationLinkURL =~ /Straubing$/,
362            'Location link contains correct value',
363        );
364
365        # Cancel the dialog.
366        $Selenium->find_element( '#EditFormCancel', 'css' )->click();
367        $Selenium->WaitFor(
368            JavaScript =>
369                'return typeof($) === "function" && !$(".Dialog:visible").length && !$(".CalendarWidget.Loading").length && $(".fc-toolbar .fc-prev-button").length;'
370        );
371
372        # Go to previous week.
373        $Selenium->find_element( '.fc-toolbar .fc-prev-button', 'css' )->click();
374        $Selenium->WaitFor(
375            JavaScript =>
376                'return !$(".CalendarWidget.Loading").length && $(".fc-timelineWeek-view .fc-slats td.fc-widget-content:nth-child(5)").length;'
377        );
378
379        # Click on the timeline view for another appointment dialog.
380        $Selenium->find_element( '.fc-timelineWeek-view .fc-slats td.fc-widget-content:nth-child(5)', 'css' )->click();
381        $Selenium->WaitFor(
382            JavaScript => "return typeof(\$) === 'function' && \$('#Title').length && \$('#CalendarID').length;"
383        );
384
385        # Enter some data.
386        $Selenium->find_element( 'Title', 'name' )->send_keys('Appointment 2');
387        $Selenium->InputFieldValueSet(
388            Element => '#CalendarID',
389            Value   => $Calendar2{CalendarID},
390        );
391
392        $Selenium->find_element( '#AllDay', 'css' )->click();
393        $Selenium->WaitFor( JavaScript => 'return typeof($) === "function" && $("#AllDay").prop("checked") === true;' );
394
395        # Click on Save.
396        $Selenium->find_element( '#EditFormSubmit', 'css' )->click();
397        $Selenium->WaitFor(
398            JavaScript =>
399                'return typeof($) === "function" && !$(".Dialog:visible").length && !$(".CalendarWidget.Loading").length;'
400        );
401        $Selenium->WaitFor(
402            JavaScript =>
403                'return $("#Calendar' . $Calendar1{CalendarID} . ':checked").length;'
404        );
405
406        # Hide the first calendar from view.
407        $Selenium->execute_script("\$('#Calendar$Calendar1{CalendarID}').click();");
408        $Selenium->WaitFor(
409            JavaScript => 'return typeof($) === "function" && $("#Calendar'
410                . $Calendar1{CalendarID}
411                . '").prop("checked") === false;'
412        );
413        $Selenium->WaitFor(
414            JavaScript =>
415                "return typeof(\$) === 'function' && \$('.fc-timeline-event .fc-title').text() === 'Appointment 2';"
416        );
417        $Selenium->WaitFor(
418            JavaScript =>
419                "return \$('.fc-timeline-event .fa-sun-o').length == 1;"
420        );
421
422        # Verify second appointment is visible.
423        $Self->Is(
424            $Selenium->execute_script(
425                "return \$('.fc-timeline-event .fc-title').text();"
426            ),
427            'Appointment 2',
428            'Second appointment visible',
429        );
430
431        # Verify second appointment is an all day appointment.
432        $Self->Is(
433            $Selenium->execute_script(
434                "return \$('.fc-timeline-event .fa-sun-o').length;"
435            ),
436            '1',
437            'Second appointment in an all day appointment',
438        );
439
440        $Selenium->WaitFor(
441            JavaScript =>
442                "return \$('.fc-timelineWeek-view .fc-slats td.fc-widget-content:nth-child(5)').length;"
443        );
444
445        # Click again on the timeline view for an appointment dialog.
446        $Selenium->find_element( '.fc-timelineWeek-view .fc-slats td.fc-widget-content:nth-child(5)', 'css' )->click();
447        $Selenium->WaitFor(
448            JavaScript => "return typeof(\$) === 'function' && \$('#Title').length && \$('#CalendarID').length;"
449        );
450
451        # Enter some data.
452        $Selenium->find_element( 'Title', 'name' )->send_keys('Appointment 3');
453        $Selenium->InputFieldValueSet(
454            Element => '#CalendarID',
455            Value   => $Calendar3{CalendarID},
456        );
457        $Selenium->find_element( 'EndHour', 'name' )->send_keys('18');
458        $Selenium->InputFieldValueSet(
459            Element => '#RecurrenceType',
460            Value   => 'Daily',
461        );
462
463        # Click on Save.
464        $Selenium->find_element( '#EditFormSubmit', 'css' )->click();
465        $Selenium->WaitFor(
466            JavaScript =>
467                'return typeof($) === "function" && !$(".Dialog:visible").length && !$(".CalendarWidget.Loading").length;'
468        );
469
470        # Wait until all AJAX calls finished.
471        $Selenium->WaitFor( JavaScript => "return \$.active == 0;" );
472
473        $Selenium->WaitFor(
474            JavaScript =>
475                'return $("#Calendar' . $Calendar2{CalendarID} . ':checked").length;'
476        );
477
478        # Hide the second calendar from view.
479        $Selenium->execute_script("\$('#Calendar$Calendar2{CalendarID}').click();");
480        $Selenium->WaitFor(
481            JavaScript => 'return typeof($) === "function" && $("#Calendar'
482                . $Calendar2{CalendarID}
483                . '").prop("checked") === false;'
484        );
485
486        # Verify all third appointment occurences are visible.
487        $Self->Is(
488            $Selenium->execute_script(
489                "return \$('.fc-timeline-event .fc-title').length;"
490            ),
491            '4',
492            'All third appointment occurrences visible',
493        );
494
495        # Click on an appointment.
496        $Selenium->execute_script(
497            "\$('.fc-timeline-event:eq(0) .fc-title').trigger('click');"
498        );
499        $Selenium->WaitFor( JavaScript => "return typeof(\$) === 'function' && \$('#EditFormDelete').length;" );
500
501        # Click on Delete.
502        $Selenium->find_element( '#EditFormDelete', 'css' )->click();
503
504        $Selenium->WaitFor( AlertPresent => 1 );
505        $Selenium->accept_alert();
506
507        # Wait for dialog to close and AJAX to finish.
508        $Selenium->WaitFor(
509            JavaScript =>
510                'return typeof($) === "function" && !$(".Dialog:visible").length && !$(".fc-timeline-event .fc-title").length;'
511        );
512
513        # Wait until all AJAX calls finished.
514        $Selenium->WaitFor( JavaScript => "return \$.active == 0;" );
515
516        # Verify all third appointment occurences have been removed.
517        $Self->Is(
518            $Selenium->execute_script(
519                "return \$('.fc-timeline-event .fc-title').length;"
520            ),
521            '0',
522            'All third appointment occurrences removed',
523        );
524
525        # Show all three calendars.
526        $Selenium->find_element( 'Calendar' . $Calendar1{CalendarID}, 'id' )->click();
527        $Selenium->WaitFor( JavaScript => 'return typeof($) === "function" && !$(".CalendarWidget.Loading").length;' );
528        $Selenium->find_element( 'Calendar' . $Calendar2{CalendarID}, 'id' )->click();
529        $Selenium->WaitFor( JavaScript => 'return typeof($) === "function" && !$(".CalendarWidget.Loading").length;' );
530
531        # Verify only two appointments are visible.
532        $Self->Is(
533            $Selenium->execute_script(
534                "return \$('.fc-timeline-event .fc-title').length;"
535            ),
536            '2',
537            'First and second appointment visible',
538        );
539
540        # Open datepicker.
541        $Selenium->find_element( '.fc-toolbar .fc-jump-button', 'css' )->click();
542        $Selenium->WaitFor(
543            JavaScript =>
544                'return typeof($) === "function" && $("#Datepicker .ui-datepicker-calendar .Highlight").length === 1;'
545        );
546
547        # Verify exactly one day with appointments is highlighted.
548        $Self->Is(
549            $Selenium->execute_script(
550                "return \$('.ui-datepicker .ui-datepicker-calendar .Highlight').length;"
551            ),
552            1,
553            'Datepicker properly highlighted',
554        );
555
556        # Close datepicker.
557        $Selenium->find_element( 'div#DatepickerOverlay', 'css' )->click();
558        $Selenium->WaitFor( JavaScript => 'return typeof($) === "function" && !$("#DatepickerOverlay").length;' );
559
560        # Filter just third calendar.
561        $Selenium->find_element( 'input#FilterCalendars', 'css' )->send_keys('Calendar3');
562        $Selenium->WaitFor(
563            JavaScript => 'return typeof($) === "function" && $(".CalendarSwitch:visible").length === 1;'
564        );
565
566        # Verify only one calendar is shown in the list.
567        $Self->Is(
568            $Selenium->execute_script(
569                "return \$('.CalendarSwitch:visible').length;"
570            ),
571            1,
572            'Calendars are filtered correctly',
573        );
574
575        # Create new Appointment (as root user).
576        my $StartTimeObject = $Kernel::OM->Create('Kernel::System::DateTime');
577        $StartTimeObject->Subtract(
578            Weeks => 1,
579        );
580        my $EndTimeObject = $StartTimeObject->Clone();
581        $EndTimeObject->Add(
582            Hours => 2,
583        );
584
585        my $AppointmentObject = $Kernel::OM->Get('Kernel::System::Calendar::Appointment');
586        my $AppointmentID     = $AppointmentObject->AppointmentCreate(
587            CalendarID  => $Calendar4{CalendarID},
588            Title       => 'Permissions check appointment',
589            Description => 'How to use Process tickets...',
590            Location    => 'Straubing',
591            StartTime   => $StartTimeObject->ToString(),
592            EndTime     => $EndTimeObject->ToString(),
593            UserID      => $UserID,
594            TimezoneID  => 0,
595        );
596
597        $Self->True(
598            $AppointmentID,
599            'Permission Appointment created.',
600        );
601
602        # Add ro permissions to the user.
603        $GroupObject->PermissionGroupUserAdd(
604            GID        => $GroupID2,
605            UID        => $UserID,
606            Permission => {
607                ro => 1,
608            },
609            UserID => $UserID,
610        );
611
612        # Reload page.
613        $Selenium->VerifiedGet("${ScriptAlias}index.pl?Action=AgentAppointmentCalendarOverview");
614        $Selenium->WaitFor( JavaScript => 'return typeof($) === "function" && !$(".CalendarWidget.Loading").length;' );
615
616        # Show the fourth calendar and hide all others.
617        $Selenium->execute_script("\$('#Calendar$Calendar4{CalendarID}').click();");
618        $Selenium->WaitFor(
619            JavaScript => 'return typeof($) === "function" && $("#Calendar'
620                . $Calendar4{CalendarID}
621                . '").prop("checked") === true;'
622        );
623        $Self->True(
624            $Selenium->execute_script(
625                'return $("#Calendar' . $Calendar4{CalendarID} . '").prop("checked") === true;'
626            ),
627            "CalendarID $Calendar4{CalendarID} - checked",
628        );
629
630        $Selenium->execute_script("\$('#Calendar$Calendar1{CalendarID}').click();");
631        $Selenium->WaitFor(
632            JavaScript => 'return typeof($) === "function" && $("#Calendar'
633                . $Calendar1{CalendarID}
634                . '").prop("checked") === false;'
635        );
636        $Self->True(
637            $Selenium->execute_script(
638                'return $("#Calendar' . $Calendar1{CalendarID} . '").prop("checked") === false;'
639            ),
640            "CalendarID $Calendar1{CalendarID} - unchecked",
641        );
642
643        $Selenium->execute_script("\$('#Calendar$Calendar2{CalendarID}').click();");
644        $Selenium->WaitFor(
645            JavaScript => 'return typeof($) === "function" && $("#Calendar'
646                . $Calendar2{CalendarID}
647                . '").prop("checked") === false;'
648        );
649        $Self->True(
650            $Selenium->execute_script(
651                'return $("#Calendar' . $Calendar2{CalendarID} . '").prop("checked") === false;'
652            ),
653            "CalendarID $Calendar2{CalendarID} - unchecked",
654        );
655
656        $Selenium->execute_script("\$('#Calendar$Calendar3{CalendarID}').click();");
657        $Selenium->WaitFor(
658            JavaScript => 'return typeof($) === "function" && $("#Calendar'
659                . $Calendar3{CalendarID}
660                . '").prop("checked") === false;'
661        );
662        $Self->True(
663            $Selenium->execute_script(
664                'return $("#Calendar' . $Calendar3{CalendarID} . '").prop("checked") === false;'
665            ),
666            "CalendarID $Calendar3{CalendarID} - unchecked",
667        );
668
669        $Selenium->WaitFor( JavaScript => 'return $(".fc-toolbar .fc-prev-button").length;' );
670
671        # Go to previous week.
672        $Selenium->find_element( '.fc-toolbar .fc-prev-button', 'css' )->click();
673        $Selenium->WaitFor(
674            JavaScript =>
675                'return typeof($) === "function" && !$(".CalendarWidget.Loading").length && $(".fc-event-container a").length;'
676        );
677
678        # Find the appointment link.
679        my $AppointmentLink = $Selenium->find_element( '.fc-event-container a', 'css' );
680
681        # Click on appointment.
682        $AppointmentLink->click();
683        $Selenium->WaitFor( JavaScript => "return typeof(\$) === 'function' && \$('#Title').length;" );
684
685        my $TeamObjectRegistered
686            = $Kernel::OM->Get('Kernel::System::Main')->Require( 'Kernel::System::Calendar::Team', Silent => 1 );
687
688        # Check if fields are disabled.
689        ELEMENT:
690        for my $Element (
691            qw(Title Description Location CalendarID TeamID ResourceID StartDay EndDay AllDay
692            RecurrenceType
693            )
694            )
695        {
696            # Check if team object is registered.
697            if ( !$TeamObjectRegistered ) {
698                next ELEMENT if $Element eq 'TeamID' || $Element eq 'ResourceID';
699            }
700
701            $ElementReadOnly->(
702                UnitTestObject => $Self,
703                Element        => $Element,
704                Value          => 1,
705            );
706        }
707
708        # Elements that are not allowed on page.
709        for my $Element (qw(EditFormSubmit EditFormDelete EditFormCopy)) {
710            $ElementExists->(
711                UnitTestObject => $Self,
712                Element        => $Element,
713                Value          => 0,
714            );
715        }
716
717        # Elements that should be on page.
718        for my $Element (qw(EditFormCancel)) {
719            $Selenium->WaitFor(
720                JavaScript =>
721                    "return typeof(\$) === 'function' && \$('#$Element').length;"
722            );
723            $ElementExists->(
724                UnitTestObject => $Self,
725                Element        => $Element,
726                Value          => 1,
727            );
728        }
729
730        # Click on cancel.
731        $Selenium->find_element( '#EditFormCancel', 'css' )->click();
732        $Selenium->WaitFor(
733            JavaScript =>
734                'return typeof($) === "function" && !$(".Dialog:visible").length && !$(".CalendarWidget.Loading").length;'
735        );
736
737        # Add move_into permissions to the user.
738        $GroupObject->PermissionGroupUserAdd(
739            GID        => $GroupID2,
740            UID        => $UserID,
741            Permission => {
742                ro        => 1,
743                move_into => 1,
744            },
745            UserID => $UserID,
746        );
747
748        # Click on appointment.
749        $AppointmentLink->click();
750        $Selenium->WaitFor( JavaScript => "return typeof(\$) === 'function' && \$('#Title').length;" );
751
752        # Check if fields are disabled.
753        for my $Element (qw( CalendarID )) {
754            $ElementReadOnly->(
755                UnitTestObject => $Self,
756                Element        => $Element,
757                Value          => 1,
758            );
759        }
760
761        # Check if fields are enabled.
762        ELEMENT:
763        for my $Element (
764            qw(Title Description Location TeamID ResourceID StartDay EndDay AllDay RecurrenceType)
765            )
766        {
767            # Check if team object is registered.
768            if ( !$TeamObjectRegistered ) {
769                next ELEMENT if $Element eq 'TeamID' || $Element eq 'ResourceID';
770            }
771
772            $ElementReadOnly->(
773                UnitTestObject => $Self,
774                Element        => $Element,
775                Value          => 0,
776            );
777        }
778
779        # Elements that are not allowed on page.
780        for my $Element (qw(EditFormDelete EditFormCopy)) {
781            $ElementExists->(
782                UnitTestObject => $Self,
783                Element        => $Element,
784                Value          => 0,
785            );
786        }
787
788        # Elements that should be on page.
789        for my $Element (qw(EditFormSubmit EditFormCancel)) {
790            $Selenium->WaitFor(
791                JavaScript =>
792                    "return typeof(\$) === 'function' && \$('#$Element').length;"
793            );
794            $ElementExists->(
795                UnitTestObject => $Self,
796                Element        => $Element,
797                Value          => 1,
798            );
799        }
800
801        # Click on cancel.
802        $Selenium->find_element( '#EditFormCancel', 'css' )->click();
803        $Selenium->WaitFor(
804            JavaScript =>
805                'return typeof($) === "function" && !$(".Dialog:visible").length && !$(".CalendarWidget.Loading").length;'
806        );
807
808        # Add create permissions to the user.
809        $GroupObject->PermissionGroupUserAdd(
810            GID        => $GroupID2,
811            UID        => $UserID,
812            Permission => {
813                ro        => 1,
814                move_into => 1,
815                create    => 1,
816            },
817            UserID => $UserID,
818        );
819
820        # Click on appointment.
821        $AppointmentLink->click();
822        $Selenium->WaitFor( JavaScript => "return typeof(\$) === 'function' && \$('#Title').length;" );
823
824        # Check if fields are enabled.
825        ELEMENT:
826        for my $Element (
827            qw(Title Description Location CalendarID TeamID ResourceID StartDay EndDay AllDay
828            RecurrenceType
829            )
830            )
831        {
832            # Check if team object is registered.
833            if ( !$TeamObjectRegistered ) {
834                next ELEMENT if $Element eq 'TeamID' || $Element eq 'ResourceID';
835            }
836
837            $ElementReadOnly->(
838                UnitTestObject => $Self,
839                Element        => $Element,
840                Value          => 0,
841            );
842        }
843
844        # Elements that should be on page.
845        for my $Element (qw(EditFormCopy EditFormSubmit EditFormDelete EditFormCancel)) {
846            $Selenium->WaitFor(
847                JavaScript =>
848                    "return typeof(\$) === 'function' && \$('#$Element').length;"
849            );
850            $ElementExists->(
851                UnitTestObject => $Self,
852                Element        => $Element,
853                Value          => 1,
854            );
855        }
856
857        # Click on cancel.
858        $Selenium->find_element( '.Close', 'css' )->click();
859        $Selenium->WaitFor(
860            JavaScript =>
861                'return typeof($) === "function" && !$(".Dialog:visible").length;'
862        );
863
864        $Selenium->find_element( '.fc-agendaWeek-button', 'css' )->click();
865
866        # Go to other screen to check if selected Timeline will be saved on return to Calendar overview.
867        # See bug#14629 - https://bugs.otrs.org/show_bug.cgi?id=14629.
868        $Selenium->VerifiedGet("${ScriptAlias}index.pl?Action=AgentTicketZoom;TicketID=${TicketID}");
869        $Selenium->VerifiedGet("${ScriptAlias}index.pl?Action=AgentAppointmentCalendarOverview");
870
871        $Self->Is(
872            $Selenium->execute_script(
873                "return \$('.fc-agendaWeek-button').hasClass('fc-state-active');"
874            ),
875            "1",
876            "Timeline is correctly selected after page reload"
877        );
878
879        #
880        # Cleanup
881        #
882
883        my $SchedulerDBObject = $Kernel::OM->Get('Kernel::System::Daemon::SchedulerDB');
884        my $DBObject          = $Kernel::OM->Get('Kernel::System::DB');
885
886        # Delete appointments and calendars.
887        for my $CalendarID (
888            $Calendar1{CalendarID},
889            $Calendar2{CalendarID},
890            $Calendar3{CalendarID},
891            $Calendar4{CalendarID},
892            )
893        {
894            my @Appointments = $AppointmentObject->AppointmentList(
895                CalendarID => $CalendarID,
896                Result     => 'ARRAY',
897            );
898            for my $AppointmentID (@Appointments) {
899                $AppointmentObject->AppointmentDelete(
900                    AppointmentID => $AppointmentID,
901                    UserID        => $UserID,
902                );
903            }
904
905            # Delete test calendars.
906            my $Success = $DBObject->Do(
907                SQL => "DELETE FROM calendar WHERE id = $CalendarID",
908            );
909            $Self->True(
910                $Success,
911                "Deleted test calendar - $CalendarID",
912            );
913        }
914
915        # Delete test ticket.
916        my $Success = $TicketObject->TicketDelete(
917            TicketID => $TicketID,
918            UserID   => $UserID,
919        );
920
921        # Ticket deletion could fail if apache still writes to ticket history. Try again in this case.
922        if ( !$Success ) {
923            sleep 3;
924            $Success = $TicketObject->TicketDelete(
925                TicketID => $TicketID,
926                UserID   => $UserID,
927            );
928        }
929        $Self->True(
930            $Success,
931            "Deleted test ticket - $TicketID",
932        );
933
934        # Remove scheduled asynchronous tasks from DB, as they may interfere with tests run later.
935        my @TaskIDs;
936        my @AllTasks = $SchedulerDBObject->TaskList(
937            Type => 'AsynchronousExecutor',
938        );
939        for my $Task (@AllTasks) {
940            if ( $Task->{Name} eq 'Kernel::System::Calendar-TicketAppointmentProcessTicket()' ) {
941                push @TaskIDs, $Task->{TaskID};
942            }
943        }
944        for my $TaskID (@TaskIDs) {
945            my $Success = $SchedulerDBObject->TaskDelete(
946                TaskID => $TaskID,
947            );
948            $Self->True(
949                $Success,
950                "TaskDelete - Removed scheduled asynchronous task $TaskID",
951            );
952        }
953
954        # Delete group-user relations.
955        $Success = $DBObject->Do(
956            SQL => "DELETE FROM group_user WHERE group_id = $GroupID OR group_id = $GroupID2",
957        );
958        if ($Success) {
959            $Self->True(
960                $Success,
961                "GroupUserDelete - $RandomID",
962            );
963        }
964
965        # Delete groups.
966        $Success = $DBObject->Do(
967            SQL => "DELETE FROM groups WHERE id = $GroupID OR id = $GroupID2",
968        );
969        $Self->True(
970            $Success,
971            "GroupDelete - $RandomID",
972        );
973
974        my $CacheObject = $Kernel::OM->Get('Kernel::System::Cache');
975
976        # Make sure cache is correct.
977        for my $Cache (qw(Appointment Calendar Group Ticket)) {
978            $CacheObject->CleanUp( Type => $Cache );
979        }
980    },
981);
982
9831;
984