blob: 09a1dda50e1c4b7f57d02f314e8a6fb48dae6c75 [file] [log] [blame]
// Copyright 2021 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package googleapps
import (
"context"
"fmt"
"regexp"
"time"
"chromiumos/tast/common/action"
"chromiumos/tast/errors"
"chromiumos/tast/local/bundles/cros/ui/cuj"
"chromiumos/tast/local/chrome"
"chromiumos/tast/local/chrome/ash"
"chromiumos/tast/local/chrome/browser"
"chromiumos/tast/local/chrome/uiauto"
"chromiumos/tast/local/chrome/uiauto/mouse"
"chromiumos/tast/local/chrome/uiauto/nodewith"
"chromiumos/tast/local/chrome/uiauto/role"
"chromiumos/tast/local/chrome/webutil"
"chromiumos/tast/local/input"
"chromiumos/tast/testing"
)
// slideName represents the name of the Google Slides web area.
const slideName = "Google Slides"
// NewGoogleSlides returns an action that creates a new google slides from web.
func NewGoogleSlides(cs ash.ConnSource, tconn *chrome.TestConn, newWindow bool) action.Action {
const newSlidesURL = "https://slides.new"
ui := uiauto.New(tconn)
var opts []browser.CreateTargetOption
if newWindow {
opts = append(opts, browser.WithNewWindow())
}
filmstripView := nodewith.Name("Filmstrip view").Role(role.TabPanel)
gotIt := nodewith.Name("Got it").First()
return uiauto.Combine("create google slide",
func(ctx context.Context) error {
testing.ContextLog(ctx, "Start to create google slide")
conn, err := cs.NewConn(ctx, newSlidesURL, opts...)
if err != nil {
return err
}
defer conn.Close()
if err := webutil.WaitForQuiescence(ctx, conn, longerUIWaitTime); err != nil {
return errors.Wrap(err, "failed to wait for page to finish loading")
}
return nil
},
ui.WithTimeout(longerUIWaitTime).WaitUntilExists(filmstripView),
uiauto.IfSuccessThen(ui.Exists(gotIt), ui.LeftClick(gotIt)),
)
}
// NewSlide returns an action that creates a new slide, edits its title and content.
func NewSlide(tconn *chrome.TestConn, kb *input.KeyboardEventWriter, title, content, pageNumber string) action.Action {
ui := uiauto.New(tconn)
newSlide := nodewith.Name("New slide (Ctrl+M)").Role(role.Button)
titleNode := nodewith.Name("title").First()
pageNumberNode := nodewith.Name(pageNumber).First()
textNode := nodewith.Name("text").First()
return uiauto.NamedAction(fmt.Sprintf("to create a new slide with page number %s and edit its content", pageNumber),
uiauto.Combine("create a new slide and edit content",
ui.WaitUntilExists(newSlide),
ui.WithTimeout(longerUIWaitTime).LeftClickUntil(newSlide, ui.WithTimeout(25*time.Second).WaitUntilExists(pageNumberNode)),
ui.WaitUntilExists(titleNode),
ui.DoubleClick(titleNode),
uiauto.Sleep(time.Second),
kb.TypeAction(title),
ui.WaitUntilExists(textNode),
ui.DoubleClick(textNode),
uiauto.Sleep(time.Second),
kb.TypeAction(content),
waitForSlideSaved(tconn),
),
)
}
// RenameSlide returns an action that renames google slide.
func RenameSlide(tconn *chrome.TestConn, kb *input.KeyboardEventWriter, title string) action.Action {
ui := uiauto.New(tconn)
slideWebArea := nodewith.NameContaining(slideName).Role(role.RootWebArea)
renameTextbox := nodewith.Name("Rename").ClassName("docs-title-input").Ancestor(slideWebArea).Editable().Focusable()
return uiauto.NamedAction("to rename the slide",
ui.Retry(5, uiauto.Combine("rename slide",
ui.WaitUntilExists(slideWebArea),
ui.LeftClickUntil(renameTextbox, ui.WithTimeout(5*time.Second).WaitUntilExists(renameTextbox.State("focused", true))),
kb.AccelAction("Ctrl+A"),
kb.TypeAction(title),
waitForFieldTextToBe(tconn, renameTextbox, title),
kb.AccelAction("Enter"),
waitForSlideSaved(tconn),
)),
)
}
// PresentSlide returns an action that presents google slide.
func PresentSlide(tconn *chrome.TestConn, kb *input.KeyboardEventWriter, slideCount int) action.Action {
ui := uiauto.New(tconn)
slideWebArea := nodewith.NameContaining(slideName).Role(role.RootWebArea)
presentationOptionsButton := nodewith.Name("Presentation options").First()
// There are two versions of ui to present slide.
presentFromBeginningButton := nodewith.NameRegex(regexp.MustCompile("(Present|Start) from beginning.*")).Role(role.MenuItem).First()
menuBar := nodewith.Name("Menu bar").Role(role.Banner).Ancestor(slideWebArea).First()
return uiauto.NamedAction("to present slide",
uiauto.Combine("present Slide",
ui.WaitUntilExists(presentationOptionsButton),
ui.LeftClickUntil(presentationOptionsButton, ui.WithTimeout(5*time.Second).WaitUntilExists(presentFromBeginningButton)),
ui.LeftClick(presentFromBeginningButton),
ui.WithTimeout(40*time.Second).WaitUntilGone(presentationOptionsButton),
func(ctx context.Context) error {
testing.ContextLog(ctx, "Switch slides")
for i := 0; i < slideCount; i++ {
if err := uiauto.Combine("present Slide",
kb.AccelAction("Enter"), // Press enter to switch slide.
uiauto.Sleep(time.Second), // Sleep to wait for slide switching.
)(ctx); err != nil {
return errors.Wrap(err, "failed to switch slide")
}
}
return nil
},
kb.AccelAction("Esc"), //Press Esc to leave presentation mode
// Some of DUT models with poor performance need to wait a long time to leave presentation mode.
ui.WithTimeout(longerUIWaitTime).WaitUntilExists(menuBar),
),
)
}
// EditSlideTitle returns an action that edits google slide title and subtitle.
func EditSlideTitle(tconn *chrome.TestConn, kb *input.KeyboardEventWriter, title, subtitle string) action.Action {
ui := uiauto.New(tconn)
titleNode := nodewith.Name("title").First()
subtitleNode := nodewith.Name("subtitle").First()
return uiauto.NamedAction("to edit slide title and subtitle",
uiauto.Combine("edit slide and subtitle",
ui.WaitUntilExists(titleNode),
ui.DoubleClick(titleNode),
uiauto.Sleep(time.Second),
kb.TypeAction(title),
ui.WaitUntilExists(subtitleNode),
ui.DoubleClick(subtitleNode),
uiauto.Sleep(time.Second),
kb.TypeAction(subtitle),
waitForSlideSaved(tconn),
),
)
}
// EditSlide returns an action that edits google slide.
func EditSlide(tconn *chrome.TestConn, kb *input.KeyboardEventWriter, text, expectedText string) action.Action {
ui := uiauto.New(tconn)
return uiauto.NamedAction("to edit slide",
uiauto.Combine("edit slide",
func(ctx context.Context) error {
nodes := nodewith.Name(text)
nodesInfo, err := ui.NodesInfo(ctx, nodes)
if err != nil {
return errors.Wrap(err, "failed to get nodes info")
}
return mouse.DoubleClick(tconn, nodesInfo[len(nodesInfo)-1].Location.CenterPoint(), 500*time.Millisecond)(ctx)
},
kb.TypeAction(expectedText),
kb.AccelAction("Esc"),
kb.AccelAction("Esc"),
waitForSlideSaved(tconn),
),
)
}
// DeleteSlide returns an action that deletes google slide.
func DeleteSlide(tconn *chrome.TestConn) action.Action {
ui := uiauto.New(tconn)
slideWebArea := nodewith.NameContaining(slideName).Role(role.RootWebArea)
slideHomeWebArea := nodewith.Name(slideName).Role(role.RootWebArea)
application := nodewith.Role(role.Application).Ancestor(slideWebArea) // Google Slide appliction node.
fileButton := nodewith.Name("File").Role(role.MenuItem).Ancestor(application)
menu := nodewith.Role(role.Menu).Ancestor(application)
moveToTrash := nodewith.NameContaining("Move to trash t").Role(role.MenuItem)
goToSlidesHome := nodewith.Name("Go to Slides home screen").Role(role.Button)
leaveButton := nodewith.Name("Leave").Role(role.Button)
return uiauto.NamedAction("to delete slide",
uiauto.Combine("delete slide",
cuj.ExpandMenu(tconn, fileButton, menu, 482),
ui.LeftClick(moveToTrash),
ui.LeftClick(goToSlidesHome),
// When leaving the edit slide, sometimes the "Leave Site?" dialog box will pop up.
// If it appears, click the leave button.
uiauto.IfSuccessThen(ui.WithTimeout(5*time.Second).WaitUntilExists(leaveButton), ui.LeftClick(leaveButton)),
ui.WithTimeout(longerUIWaitTime).WaitUntilExists(slideHomeWebArea),
),
)
}
// waitForSlideSaved waits for the slide document state to be saved.
func waitForSlideSaved(tconn *chrome.TestConn) action.Action {
return waitForDocumentSaved(tconn, slideName)
}