chromium/chrome/test/data/extensions/api_test/file_browser/handler_test_runner/test.js

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

/**
 * Runs test to verify that file browser tasks can successfully be executed.
 * The test does the following:
 * - Open external filesystem.
 * - Get the test file system. The root is determined by probing volumes with
 *   whitelisted ids.
 * - Get files 'test_dir/test_file.xul' and 'test_dir/test_file.tiff'
 *   on the test mount point.
 *   Chrome part of the test should ensure that these actually exist.
 * - For each of the files do following:
 *   - Get registered file tasks.
 *   - Verify there is exactly one registered task (Chrome part of the test
 *     should ensure this: it should launch an extension that has exactly one
 *     handler for each of files).
 *   - Execute the task.
 *
 * If there is a failure in any of these steps, the extension reports it back to
 * Chrome, otherwise it does nothing. The responsibility to report test result
 * is given to file task handler.
 */

/**
 * Files for which the file browser handlers will be executed by the extension.
 * @type {Array<string>}
 */
var kTestPaths = ['test_dir/test_file.xul', 'test_dir/test_file.tiff'];

// Starts the test extension.
function run() {
  /**
   * Test cases after the file path has been resolved to FileEntry. Each
   * resolved test case contains the resolved FileEntry object.
   *
   * @type {!Array<!FileEntry>}
   */
  var resolvedEntries = [];

  /**
   * List of tasks found for a testCase. Each object contains the found task id
   * and entry for which the task should be executed.
   *
   * @type {!Array<!Object<string, !Entry>>}
   */
  var foundTasks = [];

  /**
   * Whether the test extension has done its job. When done is set |onError|
   * calls will be ignored.
   * @type {boolean}
   */
  var done = false;

  /**
   * Function called when an error is encountered. It sends test failure
   * notification.
   *
   * @param {string} errorMessage The error message to be send.
   */
  function onError(errorMessage) {
    if (done)
      return;

    chrome.test.notifyFail(errorMessage);
    // there should be at most one notifyFail call.
    done = true;
  }

  /**
   * Callback to chrome.fileManagerPrivate.executeTask. Verifies the function
   * succeeded.
   *
   * @param {!Entry} entry The entry of file for which the handler was executed.
   * @param {boolean} success Whether the function succeeded.
   */
  function onExecuteTask(entry, success) {
    if (!success)
      onError('Failed to execute task for ' + entry.fullPath);
  }

  /**
   * Callback to chrome.fileManagerPrivate.getFileTasks.
   * It checks that the returned task is not the default, sets it as the default
   * and calls getFileTasks again.
   *
   * @param {string} entry File entry for which getFileTasks was called.
   * @param {!chrome.fileManagerPrivate.ResultingTasks} resultingTasks List of
   *     found task objects.
   */

  function onGotNonDefaultTasks(entry, resultingTasks) {
    if (!resultingTasks || !resultingTasks.tasks) {
      onError('Failed getting tasks for ' + entry.fullPath);
      return;
    }
    const tasks = resultingTasks.tasks;
    if (tasks.length != 1) {
      onError('Got invalid number of tasks for "' + entry.fullPath + '": ' +
              tasks.length);
    }
    // Task could be default from an explicit file extension match
    // but if matched on MIME type we need to set the task as default
    chrome.fileManagerPrivate.setDefaultTask(
      tasks[0].descriptor, [entry], [], function() {
          if (chrome.runtime.lastError) {
            const {appId, taskType, actionId} = tasks[0].descriptor;
            onError(`Failed to set a task to default: ${appId}|${taskType}|${
                actionId}`);
            return;
          }
          chrome.fileManagerPrivate.getFileTasks(
              [entry], [''], onGotTasks.bind(null, entry));
        });
  }

  /**
   * Callback to chrome.fileManagerPrivate.getFileTasks.
   * It remembers the returned task id and entry. When tasks for all test cases
   * are found, they are executed.
   *
   * @param {!Entry} entry File entry for which getFileTasks was called.
   * @param {!chrome.fileManagerPrivate.ResultingTasks} resultingTasks List of
   *     found task objects.
   */
  function onGotTasks(entry, resultingTasks) {
    if (!resultingTasks || !resultingTasks.tasks) {
      onError('Failed getting tasks for ' + entry.fullPath);
      return;
    }
    const tasks = resultingTasks.tasks;

    if (tasks.length != 1) {
      onError('Got invalid number of tasks for "' + entry.fullPath + '": ' +
              tasks.length);
    }
    const {appId, taskType, actionId} = tasks[0].descriptor;
    const taskId = `${appId}|${taskType}|${actionId}`;
    if (!tasks[0].isDefault) {
      onError(`Task "${taskId}" is not default for "${entry.fullPath}"`);
    }

    foundTasks.push({descriptor: tasks[0].descriptor, entry: entry});

    if (foundTasks.length == kTestPaths.length) {
      foundTasks.forEach(function(task) {
        chrome.fileManagerPrivate.executeTask(task.descriptor, [task.entry],
            onExecuteTask.bind(null, task.entry));
      });
    }
  }

  /**
   * Success callback for getFile operation. Remembers resolved test case, and
   * when all the test cases have been resolved, gets file tasks for each of
   * them.
   *
   * @param {FileEntry} isolatedEntry The file entry for the test case.
   */
  function onGotEntry(isolatedEntry) {
    // TODO(mtomasz): Remove this hack after migrating chrome.fileManagerPrivate
    // API to isolated context.
    chrome.fileManagerPrivate.resolveIsolatedEntries(
        [isolatedEntry],
        function(externalEntries) {
          resolvedEntries.push(externalEntries[0]);
          if (resolvedEntries.length == kTestPaths.length) {
            resolvedEntries.forEach(function(entry) {
              chrome.fileManagerPrivate.getFileTasks(
                  [entry], [''], onGotNonDefaultTasks.bind(null, entry));
            });
          }
        });
  }

  /**
   * Called when the test mount point has been determined. It starts resolving
   * test cases (i.e. getting file entries for the test file paths).
   *
   * @param {DOMFileSystem} fileSystem The testing volume.
   * @param {string} volumeType Type of the volume.
   */
  function onGotFileSystem(fileSystem, volumeType) {
    var isOnDrive = volumeType == 'drive';
    kTestPaths.forEach(function(filePath) {
      fileSystem.root.getFile(
          (isOnDrive ? 'root/' : '') + filePath, {},
          onGotEntry.bind(null),
          onError.bind(null, 'Unable to get file: ' + filePath));
    });
  }

  chrome.fileManagerPrivate.getVolumeMetadataList(function(volumeMetadataList) {
    // Try to acquire the first volume which is either TESTING or DRIVE type.
    var possibleVolumeTypes = ['testing', 'drive'];
    var sortedVolumeMetadataList = volumeMetadataList.filter(function(volume) {
      return possibleVolumeTypes.indexOf(volume.volumeType) != -1;
    }).sort(function(volumeA, volumeB) {
      return possibleVolumeTypes.indexOf(volumeA.volumeType) -
             possibleVolumeTypes.indexOf(volumeB.volumeType);
    });
    if (sortedVolumeMetadataList.length == 0) {
      onError('No volumes available, which could be used for testing.');
      return;
    }
    chrome.fileSystem.requestFileSystem(
        {volumeId: sortedVolumeMetadataList[0].volumeId},
        function(fileSystem) {
          if (!fileSystem) {
            onError('Failed to acquire the testing volume.');
            return;
          }
          onGotFileSystem(fileSystem, sortedVolumeMetadataList[0].volumeType);
        });
  });
}

// Start the testing.
run();