blob: c71ce1d8e9d80b264fc52c3d08864aba0c830cb6 [file] [log] [blame]
// Copyright 2022 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package ui
import (
"context"
"strings"
"github.com/golang/protobuf/ptypes/empty"
"github.com/google/go-cmp/cmp"
"google.golang.org/grpc"
"google.golang.org/protobuf/testing/protocmp"
"go.chromium.org/tast-tests/cros/remote/crosserverutil"
pb "go.chromium.org/tast-tests/cros/services/cros/ui"
"go.chromium.org/tast/core/testing"
"go.chromium.org/tast/core/testing/hwdep"
)
func init() {
testing.AddTest(&testing.Test{
Func: AutomationServiceGRPC,
LacrosStatus: testing.LacrosVariantUnneeded,
Desc: "Check basic functionalities of UI AutomationService",
Contacts: []string{"chromeos-sw-engprod@google.com", "jonfan@google.com"},
BugComponent: "b:1034649",
Attr: []string{"group:hw_agnostic"},
SoftwareDeps: []string{"chrome"},
HardwareDeps: hwdep.D(hwdep.FormFactor(hwdep.Clamshell)),
})
}
// AutomationServiceGRPC tests basic functionalities of UI AutomationService.
func AutomationServiceGRPC(ctx context.Context, s *testing.State) {
cl, err := crosserverutil.GetGRPCClient(ctx, s.DUT())
if err != nil {
s.Fatal("Failed to connect to the RPC service on the DUT: ", err)
}
defer cl.Close(ctx)
// Start Chrome on the DUT.
cs := pb.NewChromeServiceClient(cl.Conn)
loginReq := &pb.NewRequest{
ExtraArgs: []string{"--force-tablet-mode=clamshell"},
}
if _, err := cs.New(ctx, loginReq, grpc.WaitForReady(true)); err != nil {
s.Fatal("Failed to start Chrome: ", err)
}
defer cs.Close(ctx, &empty.Empty{})
uiautoSvc := pb.NewAutomationServiceClient(cl.Conn)
// Wait until both chrome app and files app buttons show up after initial login.
filesAppShelfButtonFinder := &pb.Finder{
NodeWiths: []*pb.NodeWith{
{Value: &pb.NodeWith_HasClass{HasClass: "ShelfAppButton"}},
{Value: &pb.NodeWith_Name{Name: "Files"}},
},
}
chromeAppShelfButtonFinder := &pb.Finder{
NodeWiths: []*pb.NodeWith{
{Value: &pb.NodeWith_HasClass{HasClass: "ShelfAppButton"}},
{Value: &pb.NodeWith_Name{Name: "Google Chrome"}},
},
}
if _, err := uiautoSvc.WaitUntilExists(ctx, &pb.WaitUntilExistsRequest{Finder: chromeAppShelfButtonFinder}); err != nil {
s.Fatal("Failed to find Chrome shelf button: ", err)
}
if _, err := uiautoSvc.WaitUntilExists(ctx, &pb.WaitUntilExistsRequest{Finder: filesAppShelfButtonFinder}); err != nil {
s.Fatal("Failed to find Files shelf button: ", err)
}
// Only verify screenshot captured, without screen diff validation.
// The size of the png image is above the 4MB limit imposed by the GRPC server.
maxSizeOption := grpc.MaxCallRecvMsgSize(32 * 10e6)
if _, err := uiautoSvc.CaptureScreenshot(ctx, &pb.CaptureScreenshotRequest{Finder: chromeAppShelfButtonFinder}, maxSizeOption); err != nil {
s.Fatal("Failed to take screenshot of the Chrome app shelf button : ", err)
}
// Only verify screenshot captured, without screen diff validation.
if _, err := uiautoSvc.CaptureScreenshot(ctx, &pb.CaptureScreenshotRequest{}, maxSizeOption); err != nil {
s.Fatal("Failed to take screenshot: ", err)
}
// Get UI Tree string and perform basic check.
uiTreeResponse, err := uiautoSvc.GetUITree(ctx, &pb.GetUITreeRequest{})
if err != nil {
s.Fatal("Failed to get UI Tree string: ", err)
}
if !strings.Contains(uiTreeResponse.UiTree, "className=RootWindow-0") {
s.Fatal("Failed to find RootWindow-0 in : ", uiTreeResponse.UiTree)
}
// Open Files App and close it by clicking the cross on the window.
if _, err := uiautoSvc.LeftClick(ctx, &pb.LeftClickRequest{Finder: filesAppShelfButtonFinder}); err != nil {
s.Fatal("Failed to click on Files app: ", err)
}
// Wait for search button on the files app to show up.
filesAppWindowFinder := &pb.Finder{
NodeWiths: []*pb.NodeWith{
{Value: &pb.NodeWith_HasClass{HasClass: "BrowserFrame"}},
{Value: &pb.NodeWith_Name{Name: "Files - My files"}},
},
}
filesAppSearchButtonFinder := &pb.Finder{
NodeWiths: []*pb.NodeWith{
{Value: &pb.NodeWith_HasClass{HasClass: "icon-button"}},
{Value: &pb.NodeWith_Name{Name: "Search"}},
{Value: &pb.NodeWith_Focusable{}},
{Value: &pb.NodeWith_Ancestor{
Ancestor: filesAppWindowFinder,
}},
},
}
if _, err := uiautoSvc.WaitUntilExists(ctx, &pb.WaitUntilExistsRequest{Finder: filesAppSearchButtonFinder}); err != nil {
s.Fatal("Failed to wait for files app search button: ", err)
}
// Test IsNodeFound to confirm that the search button is there.
if res, err := uiautoSvc.IsNodeFound(ctx, &pb.IsNodeFoundRequest{Finder: filesAppSearchButtonFinder}); err != nil || !res.Found {
s.Fatal("Failed to find node for files app search button: ", err)
}
// Get and verify Info for the search button.
filesAppSearchButtonInfo, err := uiautoSvc.Info(ctx, &pb.InfoRequest{Finder: filesAppSearchButtonFinder})
if err != nil {
s.Fatal("Failed to get node info for files app search button: ", err)
}
// skip comparing Location and HtmlAttributes as those are form factor specific and likely to make the test not stable.
filesAppSearchButtonInfo.NodeInfo.Location = nil
filesAppSearchButtonInfo.NodeInfo.HtmlAttributes = nil
want := &pb.NodeInfo{
ClassName: "icon-button menu-button",
Name: "Search",
Restriction: pb.Restriction_RESTRICTION_NONE,
Role: pb.Role_ROLE_BUTTON,
State: map[string]bool{"focusable": true},
}
if diff := cmp.Diff(filesAppSearchButtonInfo.NodeInfo, want, protocmp.Transform()); diff != "" {
s.Fatalf("NodeInfo mismatch for search button (-got +want):%s", diff)
}
// Close Files app by clicking the close button on the window.
// To test the functionality of Info and MouseClickAtLocation, we retrieve the location of
// the close button and clicks on that location to close the window.
fileAppCloseButtonFinder := &pb.Finder{
NodeWiths: []*pb.NodeWith{
{Value: &pb.NodeWith_HasClass{HasClass: "FrameCaptionButton"}},
{Value: &pb.NodeWith_Name{Name: "Close"}},
{Value: &pb.NodeWith_Focusable{}},
{Value: &pb.NodeWith_Ancestor{
Ancestor: filesAppWindowFinder,
}},
},
}
filesAppCloseButtonInfo, err := uiautoSvc.Info(ctx, &pb.InfoRequest{Finder: fileAppCloseButtonFinder})
if err != nil {
s.Fatal("Failed to get node info for files app close button: ", err)
}
// Getting center point of the bounding box.
r := filesAppCloseButtonInfo.NodeInfo.Location
x, y := r.Left+r.Width/2, r.Top+r.Height/2
mouseClickReq := &pb.MouseClickAtLocationRequest{
ClickType: pb.ClickType_CLICK_TYPE_LEFT_CLICK,
Point: &pb.Point{X: x, Y: y},
}
if _, err := uiautoSvc.MouseClickAtLocation(ctx, mouseClickReq); err != nil {
s.Fatalf("Failed to click at location %d,%d : %v", x, y, err)
}
// Verify that Files App window is gone.
if res, err := uiautoSvc.IsNodeFound(ctx, &pb.IsNodeFoundRequest{Finder: filesAppWindowFinder}); err != nil || res.Found {
s.Fatal("Files App window should have been gone: ", err)
}
// Open chrome app, open the context menu in the shelf by using RightClick and then close the app from the context menu item.
if _, err := uiautoSvc.LeftClick(ctx, &pb.LeftClickRequest{Finder: chromeAppShelfButtonFinder}); err != nil {
s.Fatal("Failed to click on Chrome app: ", err)
}
if _, err := uiautoSvc.RightClick(ctx, &pb.RightClickRequest{Finder: chromeAppShelfButtonFinder}); err != nil {
s.Fatal("Failed to click on Chrome app: ", err)
}
closeButton := &pb.Finder{
NodeWiths: []*pb.NodeWith{
{Value: &pb.NodeWith_Name{Name: "Close"}},
{Value: &pb.NodeWith_Role{Role: pb.Role_ROLE_MENU_ITEM}},
},
}
if _, err := uiautoSvc.WaitUntilExists(ctx, &pb.WaitUntilExistsRequest{Finder: closeButton}); err != nil {
s.Fatal("Failed to wait for close button from context menu: ", err)
}
if _, err := uiautoSvc.LeftClick(ctx, &pb.LeftClickRequest{Finder: closeButton}); err != nil {
s.Fatal("Failed to close Chrome app from context menu: ", err)
}
}