blob: 58f356bda6f93e919451cb6fe16b7ae81dad8c76 [file] [log] [blame]
// Copyright 2020 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 filemanager
import (
func init() {
Func: DragDrop,
Desc: "Verify drag drop from files app works",
Contacts: []string{
Timeout: 4 * time.Minute,
Attr: []string{"group:mainline"},
Data: []string{"drag_drop_manifest.json", "drag_drop_background.js", "drag_drop_window.js", "drag_drop_window.html"},
SoftwareDeps: []string{"chrome"},
func DragDrop(ctx context.Context, s *testing.State) {
extDir, err := ioutil.TempDir("", "tast.filemanager.DragDropExtension")
if err != nil {
s.Fatal("Failed creating temp extension directory: ", err)
defer os.RemoveAll(extDir)
dropTargetExtID, err := setupDragDropExtension(ctx, s, extDir)
if err != nil {
s.Fatal("Failed setup of drag drop extension: ", err)
cr, err := chrome.New(ctx, chrome.UnpackedExtension(extDir))
if err != nil {
s.Fatal("Failed to connect to Chrome: ", err)
defer cr.Close(ctx)
// Open the test API.
tconn, err := cr.TestAPIConn(ctx)
if err != nil {
s.Fatal("Creating test API connection failed: ", err)
defer faillog.DumpUITreeOnError(ctx, s.OutDir(), s.HasError, tconn)
// Setup the test file.
const textFile = "test.txt"
testFileLocation := filepath.Join(filesapp.MyFilesPath, textFile)
if err := ioutil.WriteFile(testFileLocation, []byte("blahblah"), 0644); err != nil {
s.Fatalf("Creating file %s failed: %s", testFileLocation, err)
defer os.Remove(testFileLocation)
// Open the Files App.
files, err := filesapp.Launch(ctx, tconn)
if err != nil {
s.Fatal("Launching the Files App failed: ", err)
// Get connection to foreground extension to verify changes.
dropTargetURL := "chrome-extension://" + dropTargetExtID + "/window.html"
conn, err := cr.NewConnForTarget(ctx, chrome.MatchTargetURL(dropTargetURL))
if err != nil {
s.Fatalf("Could not connect to extension at %v: %v", dropTargetURL, err)
defer conn.Close()
// Make sure the extension title has loaded JavaScript properly.
if err := conn.WaitForExprFailOnErrWithTimeout(ctx, "window.document.title == 'awaiting drop.'", 5*time.Second); err != nil {
s.Fatal("Failed waiting for javascript to update window.document.title: ", err)
kb, err := input.Keyboard(ctx)
if err != nil {
s.Fatal("Failed to get keyboard: ", err)
defer kb.Close()
// The drag drop chrome app defaults to (0,0) with width 300 and height 300 and always on top.
dstPoint := coords.Point{X: 100, Y: 100}
// The Files App may show a welcome banner on launch to introduce the user to new features.
// Increase polling options to give UI more time to stabilize in the event that a banner is shown.
dragDropAction := files.WithTimeout(5*time.Second).WithInterval(time.Second).DragAndDropFile(textFile, dstPoint, kb)
if err := files.PerformActionAndRetryMaximizedOnFail(dragDropAction)(ctx); err != nil {
s.Fatal("Failed to drag and drop: ", err)
if err := verifyDroppedFileMatchesDraggedFile(ctx, conn, textFile); err != nil {
s.Fatal("Failed verifying the dropped file matches the drag file: ", err)
// setupDragDropExtension moves the extension files into the extension directory and returns the extension ID.
func setupDragDropExtension(ctx context.Context, s *testing.State, extDir string) (string, error) {
for _, name := range []string{"manifest.json", "background.js", "window.js", "window.html"} {
if err := fsutil.CopyFile(s.DataPath("drag_drop_"+name), filepath.Join(extDir, name)); err != nil {
return "", errors.Wrapf(err, "failed to copy extension %q: %v", name, err)
extID, err := chrome.ComputeExtensionID(extDir)
if err != nil {
return "", errors.Wrapf(err, "failed to compute extension ID for %q: %v", extDir, err)
return extID, nil
// verifyDroppedFileMatchesDraggedFile observes the extensions window title for changes.
// If the title changes to drop registered, the file names are compared to ensure file data is transferred.
func verifyDroppedFileMatchesDraggedFile(ctx context.Context, conn *chrome.Conn, createdFileName string) error {
if err := conn.WaitForExprFailOnErrWithTimeout(ctx, "window.document.title.startsWith('drop registered:')", 5*time.Second); err != nil {
return errors.Wrap(err, "failed registering drop on extension, title has not changed")
var actualDroppedFileName string
if err := conn.Eval(ctx, "window.document.title.replace('drop registered:', '')", &actualDroppedFileName); err != nil {
return errors.Wrap(err, "failed retrieving the drag drop window title")
if createdFileName != actualDroppedFileName {
return errors.Errorf("failed dropped file doesnt match dragged file, got: %q; want: %q", actualDroppedFileName, createdFileName)
return nil