chromium/chrome/browser/ui/views/javascript_tab_modal_dialog_view_views_browsertest_mac.mm

// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#import <Accessibility/Accessibility.h>

#include <string>

#include "base/functional/bind.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/views/javascript_tab_modal_dialog_view_views.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "content/public/test/browser_test.h"
#include "ui/accessibility/platform/ax_platform_node_mac.h"
#include "ui/views/accessibility/view_accessibility.h"
#include "ui/views/bubble/bubble_frame_view.h"

using JavaScriptTabModalDialogViewViewsBrowserTestMac = InProcessBrowserTest;

IN_PROC_BROWSER_TEST_F(JavaScriptTabModalDialogViewViewsBrowserTestMac,
                       AlertDialogAccessibleNameDescriptionAndRole) {
  std::u16string title = u"Title";
  std::u16string message = u"The message";
  auto* dialog_views =
      JavaScriptTabModalDialogViewViews::CreateAlertDialogForTesting(
          browser(), title, message);

  // For a JavaScript alert dialog, VoiceOver speaks the accessible name of
  // the native window followed by the accessible name of the RootView. For
  // reasons detailed below, we have to make some adjustments on the Mac to
  // ensure that the window's name contains the title (e.g. "url.com says")
  // and that the RootView's name contains the message text. This test verifies
  // this exposure as well as exposure through other properties that VoiceOver
  // ignores.

  // The RootView of a JavaScript alert dialog should have the accessible role
  // of dialog. On the Mac, that is exposed as the subrole of a group.
  gfx::NativeViewAccessible native_dialog = dialog_views->GetWidget()
                                                ->GetRootView()
                                                ->GetViewAccessibility()
                                                .GetNativeObject();
  EXPECT_EQ(NSAccessibilityGroupRole, [native_dialog accessibilityRole]);
  EXPECT_TRUE([@"AXApplicationDialog"
      isEqualToString:(NSString*)[native_dialog accessibilitySubrole]]);

  // JavaScriptTabModalDialogViewViews sets the accessible description of the
  // RootView to the message contents. That description is set with
  // kDescriptionFrom set to kAriaDescription, which is exposed in
  // accessibilityCustomContent.
  NSString* description = nil;
  ASSERT_TRUE(
      [native_dialog conformsToProtocol:@protocol(AXCustomContentProvider)]);
  auto element_with_content =
      static_cast<id<AXCustomContentProvider>>(native_dialog);
  for (AXCustomContent* content in element_with_content
           .accessibilityCustomContent) {
    if ([content.label isEqualToString:@"description"]) {
      // There should be only one AXCustomContent with the label
      // "description".
      EXPECT_EQ(description, nil);
      description = content.value;
    }
  }
  EXPECT_EQ(message, base::SysNSStringToUTF16(description));

  // While some screen readers use the accessible description to know what to
  // present to the user, VoiceOver currently does not. Therefore, we set the
  // accessibilityLabel of the native window to contain both the title and
  // the message so that both are presented to the user.
  gfx::NativeViewAccessible native_window = [native_dialog accessibilityParent];
  EXPECT_EQ(NSAccessibilityWindowRole, [native_window accessibilityRole]);
  EXPECT_EQ(title + u", " + message,
            base::SysNSStringToUTF16([native_window accessibilityLabel]));
}