File: MonthCalendarTests.cs
Web Access
Project: src\src\System.Windows.Forms\tests\IntegrationTests\UIIntegrationTests\System.Windows.Forms.UI.IntegrationTests.csproj (System.Windows.Forms.UI.IntegrationTests)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System.Drawing;
using Windows.Win32.UI.Input.KeyboardAndMouse;
using Xunit.Abstractions;
using static System.Windows.Forms.MonthCalendar;
 
namespace System.Windows.Forms.UITests;
 
public class MonthCalendarTests : ControlTestBase
{
    private static readonly DateTime s_currentDate = new(2021, 12, 1);
    private static readonly DateTime s_maxDate = new(2021, 12, 3, 18, 0, 0);
    private static readonly DateTime s_minDate = new(2021, 11, 30, 17, 0, 0);
 
    public MonthCalendarTests(ITestOutputHelper testOutputHelper)
        : base(testOutputHelper)
    {
    }
 
    [WinFormsFact]
    public async Task MonthCalendar_SetDate_DoesntCrashApplication_IfUseMouseAsync()
    {
        await RunTestAsync(async (form, calendar) =>
        {
            await MoveMouseToControlAsync(calendar);
            await InputSimulator.SendAsync(
                form,
                inputSimulator => inputSimulator.Mouse.LeftButtonClick());
        });
    }
 
    [WinFormsFact]
    public async Task MonthCalendar_SetDate_DoesntCrashApplication_IfUseKeyboardAsync()
    {
        await RunTestAsync(async (form, calendar) =>
        {
            await InputSimulator.SendAsync(
                form,
                inputSimulator => inputSimulator.Keyboard
                                                .KeyPress(VIRTUAL_KEY.VK_RIGHT)
                                                .KeyPress(VIRTUAL_KEY.VK_RIGHT));
        });
    }
 
    [WinFormsFact]
    public async Task MonthCalendar_SetDate_DoesntCrashApplication_ProgrammaticallyAsync()
    {
        await RunTestAsync(async (form, calendar) =>
        {
            await InputSimulator.SendAsync(
                form,
                inputSimulator => inputSimulator.Keyboard
                                                .KeyPress(VIRTUAL_KEY.VK_RIGHT)
                                                .KeyPress(VIRTUAL_KEY.VK_RIGHT));
 
            DateTime selectedDate = new(2020, 4, 10);
            SYSTEMTIME date = new()
            {
                wYear = (ushort)selectedDate.Year,
                wMonth = (ushort)selectedDate.Month,
                wDay = (ushort)selectedDate.Day
            };
 
            NMSELCHANGE lParam = new()
            {
                nmhdr = new NMHDR
                {
                    code = PInvoke.MCN_SELCHANGE,
                },
                stSelStart = date,
                stSelEnd = date,
            };
 
            PInvokeCore.SendMessage(calendar, MessageId.WM_REFLECT_NOTIFY, 0, ref lParam);
        });
    }
 
    [WinFormsTheory]
    [InlineData(-1)] // Min date
    [InlineData(1)]
    [InlineData(2)] // Max date
    public async Task MonthCalendar_Click_Date_InvokeEventsAsync(int delta)
    {
        await RunClickTestAsync(async (form, calendar) =>
        {
            calendar.SetDate(s_currentDate);
            DateTime newDate = s_currentDate.Date.AddDays(delta);
            int callDateSelectedCount = 0;
            int callDateChangedCount = 0;
            calendar.DateSelected += (object? sender, DateRangeEventArgs e) => callDateSelectedCount++;
            calendar.DateChanged += (object? sender, DateRangeEventArgs e) => callDateChangedCount++;
            await ClickOnDateAsync(form, calendar, newDate);
 
            Assert.Equal(newDate.Date, calendar.SelectionStart.Date);
            Assert.NotEqual(0, callDateSelectedCount);
            Assert.NotEqual(0, callDateChangedCount);
        });
    }
 
    [WinFormsTheory]
    [InlineData(-1)] // Min date
    [InlineData(1)]
    [InlineData(2)] // Max date
    public async Task MonthCalendar_DoubleClick_Date_InvokeEventsAsync(int delta)
    {
        await RunClickTestAsync(async (form, calendar) =>
        {
            calendar.SetDate(s_currentDate);
            DateTime newDate = s_currentDate.Date.AddDays(delta);
            int callDateSelectedCount = 0;
            int callDateChangedCount = 0;
            calendar.DateSelected += (object? sender, DateRangeEventArgs e) => callDateSelectedCount++;
            calendar.DateChanged += (object? sender, DateRangeEventArgs e) => callDateChangedCount++;
            await ClickOnDateTwiceAsync(form, calendar, newDate);
 
            Assert.Equal(newDate.Date, calendar.SelectionStart.Date);
            Assert.NotEqual(0, callDateSelectedCount);
            Assert.NotEqual(0, callDateChangedCount);
        });
    }
 
    [WinFormsTheory]
    [InlineData(2018, 12, 8, (int)MCGRIDINFO_PART.MCGIP_NEXT, 2019, 1, 1)]
    [InlineData(2018, 12, 8, (int)MCGRIDINFO_PART.MCGIP_PREV, 2018, 11, 1)]
    public async Task MonthCalendar_Click_ToMonthAsync(int givenYear, int givenMonth, int givenDay, int action, int expectedYear, int expectedMonth, int expectedDay)
    {
        await RunTestAsync(async (form, calendar) =>
        {
            DateTime givenDate = new(givenYear, givenMonth, givenDay);
            DateTime expectedDate = new(expectedYear, expectedMonth, expectedDay);
 
            calendar.TodayDate = givenDate;
            calendar.SetDate(givenDate);
 
            Assert.Equal(new DateTime(givenYear, givenMonth, 1), calendar.GetDisplayRange(visible: true).Start);
 
            // Find the position of the 'Next' button
            var rect = GetCalendarGridRect(calendar, (MCGRIDINFO_PART)action);
 
            // Move the mouse to the center of the 'Next' or 'Prev' buttons
            var centerOfRect = GetCenter(rect);
            var centerOnScreen = calendar.PointToScreen(centerOfRect);
            await MoveMouseAsync(form, centerOnScreen);
 
            TaskCompletionSource<VoidResult> dateChanged = new(TaskCreationOptions.RunContinuationsAsynchronously);
            calendar.DateChanged += (sender, e) => dateChanged.TrySetResult(default);
 
            await InputSimulator.SendAsync(
                form,
                inputSimulator => inputSimulator.Mouse.LeftButtonClick());
 
            await dateChanged.Task;
 
            // Verify that the next month is selected
            Assert.Equal(expectedDate, calendar.GetDisplayRange(visible: true).Start);
        });
 
        static unsafe Rectangle GetCalendarGridRect(MonthCalendar control, MCGRIDINFO_PART part)
        {
            MCGRIDINFO result = new()
            {
                cbSize = (uint)sizeof(MCGRIDINFO),
                dwPart = part,
                dwFlags = MCGRIDINFO_FLAGS.MCGIF_RECT,
            };
 
            Assert.NotEqual(default, PInvokeCore.SendMessage(control, PInvoke.MCM_GETCALENDARGRIDINFO, default, ref result));
            return result.rc;
        }
    }
 
    private Point GetCellPositionByDate(MonthCalendar calendar, DateTime dateTime)
    {
        MonthCalendarAccessibleObject accessibleObject = (MonthCalendarAccessibleObject)calendar.AccessibilityObject;
        CalendarCellAccessibleObject cell = accessibleObject.TestAccessor().Dynamic.GetCellByDate(dateTime.Date);
        return cell.Bounds.Location + (cell.Bounds.Size / 2);
    }
 
    private async Task ClickOnDateAsync(Form form, MonthCalendar calendar, DateTime date)
    {
        await MoveMouseAsync(form, GetCellPositionByDate(calendar, date));
        await InputSimulator.SendAsync(
            form,
            inputSimulator => inputSimulator.Mouse.LeftButtonClick());
    }
 
    private async Task ClickOnDateTwiceAsync(Form form, MonthCalendar calendar, DateTime date)
    {
        await MoveMouseAsync(form, GetCellPositionByDate(calendar, date));
        await InputSimulator.SendAsync(
            form,
            inputSimulator => inputSimulator.Mouse
                                            .LeftButtonClick()
                                            .Sleep(TimeSpan.FromMilliseconds(500))
                                            .LeftButtonClick());
    }
 
    private async Task RunClickTestAsync(Func<Form, MonthCalendar, Task> runTest)
    {
        await RunSingleControlTestAsync(
            testDriverAsync: runTest,
            createControl: () =>
            {
                MonthCalendar control = new()
                {
                    Location = new Point(0, 0),
                    MinDate = s_minDate,
                    MaxDate = s_maxDate
                };
 
                return control;
            },
            createForm: () =>
            {
                return new()
                {
                    Size = new(500, 300),
                };
            });
    }
 
    private async Task RunTestAsync(Func<Form, MonthCalendar, Task> runTest)
    {
        await RunSingleControlTestAsync(
            testDriverAsync: runTest,
            createControl: () =>
            {
                MonthCalendar control = new()
                {
                    Location = new Point(0, 0),
                };
 
                return control;
            },
            createForm: () =>
            {
                return new()
                {
                    Size = new(500, 300),
                };
            });
    }
}