Commit 350b8006 authored by Kelvin Jiang's avatar Kelvin Jiang Committed by Commit Bot

[Extensions] Separate activity log into multiple files

activity_log.js will be split into activity_log_history.js and
activity_log.js to separate the file in preparation for the real-time
stream implementation.

activity_log.js takes care of the UI lifecycle and common UI elements
logic (i.e. back/clear button, search bar, and loading the activity log
when the page is reached)

activity_log_history.js takes care of fetching, handling and displaying
activity log data from the activityLogPrivate API.

In a later CL, the activity log stream (implemented by listening to
activityLogPrivate.onExtensionActivity events) will be in a different
tab under activity_log.js and will have its own file. This CL aims to
prevent putting all activity logic into one large (and very bloated)
file.

Bug: 832354
Change-Id: I88be6309ba4c4d73e0078ce86e893dafb26594c2
Reviewed-on: https://chromium-review.googlesource.com/c/1432939
Commit-Queue: Kelvin Jiang <kelvinjiang@chromium.org>
Reviewed-by: default avatarDemetrios Papadopoulos <dpapad@chromium.org>
Cr-Commit-Position: refs/heads/master@{#629848}
parent b3c44a47
...@@ -48,10 +48,16 @@ grit("flattened_resources") { ...@@ -48,10 +48,16 @@ grit("flattened_resources") {
] ]
output_dir = "$root_gen_dir/chrome/browser/resources/md_extensions" output_dir = "$root_gen_dir/chrome/browser/resources/md_extensions"
} }
js_type_check("closure_compile") {
group("closure_compile") {
deps = [
":extensions_resources",
"activity_log:closure_compile",
]
}
js_type_check("extensions_resources") {
deps = [ deps = [
":activity_log",
":activity_log_item",
":code_section", ":code_section",
":detail_view", ":detail_view",
":drag_and_drop_handler", ":drag_and_drop_handler",
...@@ -91,26 +97,6 @@ js_library("code_section") { ...@@ -91,26 +97,6 @@ js_library("code_section") {
externs_list = [ "$externs_path/developer_private.js" ] externs_list = [ "$externs_path/developer_private.js" ]
} }
js_library("activity_log_item") {
deps = [
"//ui/webui/resources/js:cr",
]
externs_list = [ "$externs_path/activity_log_private.js" ]
}
js_library("activity_log") {
deps = [
":activity_log_item",
"//ui/webui/resources/cr_elements/cr_search_field:cr_search_field",
"//ui/webui/resources/js:cr",
"//ui/webui/resources/js/cr/ui:focus_without_ink",
]
externs_list = [
"$externs_path/activity_log_private.js",
"$externs_path/developer_private.js",
]
}
js_library("detail_view") { js_library("detail_view") {
deps = [ deps = [
":item", ":item",
...@@ -258,8 +244,6 @@ js_library("load_error") { ...@@ -258,8 +244,6 @@ js_library("load_error") {
js_library("manager") { js_library("manager") {
deps = [ deps = [
":activity_log",
":activity_log_item",
":detail_view", ":detail_view",
":item", ":item",
":item_list", ":item_list",
...@@ -271,6 +255,9 @@ js_library("manager") { ...@@ -271,6 +255,9 @@ js_library("manager") {
":service", ":service",
":sidebar", ":sidebar",
":toolbar", ":toolbar",
"activity_log:activity_log",
"activity_log:activity_log_history",
"activity_log:activity_log_item",
"//ui/webui/resources/cr_elements/cr_drawer:cr_drawer", "//ui/webui/resources/cr_elements/cr_drawer:cr_drawer",
"//ui/webui/resources/cr_elements/cr_view_manager:cr_view_manager", "//ui/webui/resources/cr_elements/cr_view_manager:cr_view_manager",
"//ui/webui/resources/js:assert", "//ui/webui/resources/js:assert",
...@@ -334,13 +321,14 @@ js_library("runtime_hosts_dialog") { ...@@ -334,13 +321,14 @@ js_library("runtime_hosts_dialog") {
js_library("service") { js_library("service") {
deps = [ deps = [
":activity_log",
":error_page", ":error_page",
":item", ":item",
":load_error", ":load_error",
":navigation_helper", ":navigation_helper",
":pack_dialog", ":pack_dialog",
":toolbar", ":toolbar",
"activity_log:activity_log",
"activity_log:activity_log_history",
"//ui/webui/resources/js:assert", "//ui/webui/resources/js:assert",
"//ui/webui/resources/js:cr", "//ui/webui/resources/js:cr",
"//ui/webui/resources/js:load_time_data", "//ui/webui/resources/js:load_time_data",
......
# Copyright 2019 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import("//third_party/closure_compiler/compile_js.gni")
js_type_check("closure_compile") {
deps = [
":activity_log",
":activity_log_history",
":activity_log_item",
]
}
js_library("activity_log_item") {
deps = [
"//ui/webui/resources/js:cr",
]
externs_list = [ "$externs_path/activity_log_private.js" ]
}
js_library("activity_log_history") {
deps = [
":activity_log_item",
"//ui/webui/resources/js:cr",
]
externs_list = [ "$externs_path/activity_log_private.js" ]
}
js_library("activity_log") {
deps = [
":activity_log_history",
"..:navigation_helper",
"//ui/webui/resources/cr_elements:cr_container_shadow_behavior",
"//ui/webui/resources/cr_elements/cr_search_field:cr_search_field",
"//ui/webui/resources/js:cr",
"//ui/webui/resources/js/cr/ui:focus_without_ink",
]
externs_list = [
"$externs_path/activity_log_private.js",
"$externs_path/developer_private.js",
]
}
...@@ -7,21 +7,13 @@ ...@@ -7,21 +7,13 @@
<link rel="import" href="chrome://resources/html/cr.html"> <link rel="import" href="chrome://resources/html/cr.html">
<link rel="import" href="chrome://resources/html/promise_resolver.html"> <link rel="import" href="chrome://resources/html/promise_resolver.html">
<link rel="import" href="chrome://resources/polymer/v1_0/paper-icon-button/paper-icon-button-light.html"> <link rel="import" href="chrome://resources/polymer/v1_0/paper-icon-button/paper-icon-button-light.html">
<link rel="import" href="activity_log_item.html"> <link rel="import" href="activity_log_history.html">
<link rel="import" href="navigation_helper.html"> <link rel="import" href="../navigation_helper.html">
<link rel="import" href="shared_style.html"> <link rel="import" href="../shared_style.html">
<dom-module id="extensions-activity-log"> <dom-module id="extensions-activity-log">
<template> <template>
<style include="iron-flex cr-shared-style shared-style paper-button-style"> <style include="iron-flex cr-shared-style shared-style paper-button-style">
.activity-message {
color: #6e6e6e;
font-size: 123%; /* Should be 16px when 100% is 13px. */
font-weight: 500;
margin-top: 80px;
text-align: center;
}
#activity-log-heading { #activity-log-heading {
flex-grow: 1; flex-grow: 1;
margin-inline-start: 16px; margin-inline-start: 16px;
...@@ -51,23 +43,11 @@ ...@@ -51,23 +43,11 @@
$i18n{clearActivities} $i18n{clearActivities}
</paper-button> </paper-button>
</div> </div>
<div id="loading-activities" class="activity-message" <template is="dom-if" if="[[showHistory_]]" restamp>
hidden$="[[!shouldShowLoadingMessage_( <activity-log-history extension-id="[[extensionId]]"
pageState_)]]"> delegate="[[delegate]]" last-search="[[lastSearch_]]">
<span>$i18n{loadingActivities}</span> </activity-log-history>
</div> </template>
<div id="no-activities" class="activity-message"
hidden$="[[!shouldShowEmptyActivityLogMessage_(
pageState_, activityData_)]]">
<span>$i18n{noActivities}</span>
</div>
<div id="activity-list"
hidden$="[[!shouldShowActivities_(pageState_, activityData_)]]">
<template is="dom-repeat" items="[[activityData_]]">
<activity-log-item id="[[item.apiCall]]" data="[[item]]">
</activity-log-item>
</template>
</div>
</div> </div>
</div> </div>
</template> </template>
......
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
cr.define('extensions', function() {
'use strict';
const ActivityLog = Polymer({
is: 'extensions-activity-log',
behaviors: [
CrContainerShadowBehavior,
],
properties: {
/** @type {!string} */
extensionId: String,
/** @type {!extensions.ActivityLogDelegate} */
delegate: Object,
/** @private */
lastSearch_: {
type: String,
value: '',
},
/** @private */
showHistory_: {
type: Boolean,
value: true,
},
},
listeners: {
'view-enter-start': 'onViewEnterStart_',
'view-exit-finish': 'onViewExitFinish_',
},
/**
* Focuses the back button when page is loaded.
* @private
*/
onViewEnterStart_: function() {
this.showHistory_ = true;
Polymer.RenderStatus.afterNextRender(
this, () => cr.ui.focusWithoutInk(this.$.closeButton));
},
/**
* Set |showHistory_| to false to remove activity-log-history from the DOM.
* @private
*/
onViewExitFinish_: function() {
this.showHistory_ = false;
},
/** @private */
onClearButtonTap_: function() {
const activityLogHistory = this.$$('activity-log-history');
activityLogHistory.clearActivities();
},
/** @private */
onCloseButtonTap_: function() {
extensions.navigation.navigateTo(
{page: Page.DETAILS, extensionId: this.extensionId});
},
/**
* @private
* @param {!CustomEvent<string>} e
*/
onSearchChanged_: function(e) {
// Remove all whitespaces from the search term, as API call names and
// urls should not contain any whitespace. As of now, only single term
// search queries are allowed.
const searchTerm = e.detail.replace(/\s+/g, '');
if (searchTerm === this.lastSearch_) {
return;
}
this.lastSearch_ = searchTerm;
},
});
return {
ActivityLog: ActivityLog,
};
});
<link rel="import" href="chrome://resources/html/polymer.html">
<link rel="import" href="chrome://resources/html/cr.html">
<link rel="import" href="chrome://resources/html/promise_resolver.html">
<link rel="import" href="activity_log_item.html">
<dom-module id="activity-log-history">
<template>
<style>
#loading-activities,
#no-activities {
color: var(--md-loading-message-color);
font-size: 123%; /* Should be 16px when 100% is 13px. */
font-weight: 500;
margin-top: 80px;
text-align: center;
}
</style>
<div id="loading-activities" hidden$="[[!shouldShowLoadingMessage_(
pageState_)]]">
<span>$i18n{loadingActivities}</span>
</div>
<div id="no-activities" hidden$="[[!shouldShowEmptyActivityLogMessage_(
pageState_, activityData_)]]">
<span>$i18n{noActivities}</span>
</div>
<div id="activity-list"
hidden$="[[!shouldShowActivities_(pageState_, activityData_)]]">
<template is="dom-repeat" items="[[activityData_]]">
<activity-log-item id="[[item.apiCall]]" data="[[item]]">
</activity-log-item>
</template>
</div>
</template>
<script src="activity_log_history.js"></script>
</dom-module>
// Copyright 2018 The Chromium Authors. All rights reserved. // Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
...@@ -178,12 +178,8 @@ cr.define('extensions', function() { ...@@ -178,12 +178,8 @@ cr.define('extensions', function() {
}); });
} }
const ActivityLog = Polymer({ const ActivityLogHistory = Polymer({
is: 'extensions-activity-log', is: 'activity-log-history',
behaviors: [
CrContainerShadowBehavior,
],
properties: { properties: {
/** @type {!string} */ /** @type {!string} */
...@@ -196,78 +192,49 @@ cr.define('extensions', function() { ...@@ -196,78 +192,49 @@ cr.define('extensions', function() {
* An array representing the activity log. Stores activities grouped by * An array representing the activity log. Stores activities grouped by
* API call or content script name sorted in descending order of the call * API call or content script name sorted in descending order of the call
* count. * count.
* @private * @private {!Array<!extensions.ActivityGroup>}
* @type {!Array<!extensions.ActivityGroup>}
*/ */
activityData_: { activityData_: {
type: Array, type: Array,
value: () => [], value: () => [],
}, },
/** /** @private {ActivityLogPageState} */
* @private
* @type {ActivityLogPageState}
*/
pageState_: { pageState_: {
type: String, type: String,
value: ActivityLogPageState.LOADING, value: ActivityLogPageState.LOADING,
}, },
/** lastSearch: {
* A promise resolver for any external files waiting for the
* GetExtensionActivity API call to finish.
* Currently only used for extension_settings_browsertest.cc
* @type {!PromiseResolver}
*/
onDataFetched: {type: Object, value: new PromiseResolver()},
/** @private */
lastSearch_: {
type: String, type: String,
value: '', observer: 'onSearchChanged_',
}, }
}, },
/** @private {?number} */
navigationListener_: null,
listeners: { listeners: {
'delete-activity-log-item': 'deleteItem_', 'delete-activity-log-item': 'deleteItem_',
'view-enter-start': 'onViewEnterStart_',
}, },
/** @override */ /**
attached: function() { * A promise resolver for any external files waiting for the
// Fetch the activity log for the extension when this page is attached. * GetExtensionActivity API call to finish.
// This is necesary as the listener below is not fired if the user * Currently only used for extension_settings_browsertest.cc
// navigates directly to the activity log page using url. * @private {PromiseResolver}
this.getActivityLog_(); */
dataFetchedResolver_: null,
// Add a listener here so we fetch the activity log whenever a user
// navigates to the activity log from another page. This is needed since
// this component already exists in the background if a user navigates
// away from this page so attached may not be called when a user navigates
// back.
this.navigationListener_ = extensions.navigation.addListener(newPage => {
if (newPage.page === Page.ACTIVITY_LOG) {
this.getActivityLog_();
}
});
},
/** @override */
detached: function() {
assert(extensions.navigation.removeListener(this.navigationListener_));
this.navigationListener_ = null;
},
/** /**
* Focuses the back button when page is loaded. * Expose only the promise of dataFetchedResolver_.
* @private * @return {!Promise<void>}
*/ */
onViewEnterStart_: function() { whenDataFetched: function() {
Polymer.RenderStatus.afterNextRender( return this.dataFetchedResolver_.promise;
this, () => cr.ui.focusWithoutInk(this.$.closeButton)); },
/** @override */
attached: function() {
this.dataFetchedResolver_ = new PromiseResolver();
this.refreshActivities_();
}, },
/** /**
...@@ -296,31 +263,27 @@ cr.define('extensions', function() { ...@@ -296,31 +263,27 @@ cr.define('extensions', function() {
this.activityData_.length > 0; this.activityData_.length > 0;
}, },
/** @private */ clearActivities: function() {
onClearButtonTap_: function() {
this.delegate.deleteActivitiesFromExtension(this.extensionId).then(() => { this.delegate.deleteActivitiesFromExtension(this.extensionId).then(() => {
this.processActivities_([]); this.processActivities_([]);
}); });
}, },
/** @private */ /**
* @private
* @param {!CustomEvent<!Array<string>>} e
*/
deleteItem_: function(e) { deleteItem_: function(e) {
const activityIds = e.detail.activityIds; const activityIds = e.detail;
this.delegate.deleteActivitiesById(activityIds).then(() => { this.delegate.deleteActivitiesById(activityIds).then(() => {
// It is possible for multiple activities displayed to have the same // It is possible for multiple activities displayed to have the same
// underlying activity ID. This happens when we split content script and // underlying activity ID. This happens when we split content script and
// web request activities by fields other than their API call. For // web request activities by fields other than their API call. For
// consistency, we will re-fetch the activity log. // consistency, we will re-fetch the activity log.
this.refreshActivities(); this.refreshActivities_();
}); });
}, },
/** @private */
onCloseButtonTap_: function() {
extensions.navigation.navigateTo(
{page: Page.DETAILS, extensionId: this.extensionId});
},
/** /**
* @private * @private
* @param {!Array<!chrome.activityLogPrivate.ExtensionActivity>} * @param {!Array<!chrome.activityLogPrivate.ExtensionActivity>}
...@@ -330,18 +293,21 @@ cr.define('extensions', function() { ...@@ -330,18 +293,21 @@ cr.define('extensions', function() {
this.pageState_ = ActivityLogPageState.LOADED; this.pageState_ = ActivityLogPageState.LOADED;
this.activityData_ = this.activityData_ =
sortActivitiesByCallCount(groupActivities(activityData)); sortActivitiesByCallCount(groupActivities(activityData));
if (!this.onDataFetched.isFulfilled) { if (!this.dataFetchedResolver_.isFulfilled) {
this.onDataFetched.resolve(); this.dataFetchedResolver_.resolve();
} }
}, },
/** @return {!Promise<void>} */ /**
refreshActivities: function() { * @private
if (this.lastSearch_ === '') { * @return {!Promise<void>}
*/
refreshActivities_: function() {
if (this.lastSearch === '') {
return this.getActivityLog_(); return this.getActivityLog_();
} }
return this.getFilteredActivityLog_(this.lastSearch_); return this.getFilteredActivityLog_(this.lastSearch);
}, },
/** /**
...@@ -370,23 +336,27 @@ cr.define('extensions', function() { ...@@ -370,23 +336,27 @@ cr.define('extensions', function() {
}); });
}, },
/** @private */ /**
onSearchChanged_: function(e) { * @private
// Remove all whitespaces from the search term, as API call names and * @param {string} newSearch
// urls should not contain any whitespace. As of now, only single term * @param {string|undefined} oldSearch
// search queries are allowed. */
const searchTerm = e.detail.replace(/\s+/g, ''); onSearchChanged_: function(newSearch, oldSearch) {
if (searchTerm === this.lastSearch_) { // |this.delegate| may be undefined if --disable-features=WebUIPolymer2
return; // is set (happens on the chromeos tryjob), which causes an error. This
// happens only for the first time the observer for |lastSearch| is
// invoked, when |lastSearch| changes value from |undefined| to the
// initial value set in activity_log.js.
// TODO(kelvinjiang): Remove this when migration to Polymer 2 is complete
// (crbug.com/738611).
if (oldSearch != undefined) {
this.refreshActivities_();
} }
this.lastSearch_ = searchTerm;
this.refreshActivities();
}, },
}); });
return { return {
ActivityLog: ActivityLog, ActivityLogHistory: ActivityLogHistory,
ActivityLogDelegate: ActivityLogDelegate, ActivityLogDelegate: ActivityLogDelegate,
}; };
}); });
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
<link rel="import" href="chrome://resources/html/cr.html"> <link rel="import" href="chrome://resources/html/cr.html">
<link rel="import" href="chrome://resources/polymer/v1_0/iron-collapse/iron-collapse.html"> <link rel="import" href="chrome://resources/polymer/v1_0/iron-collapse/iron-collapse.html">
<link rel="import" href="chrome://resources/polymer/v1_0/paper-icon-button/paper-icon-button-light.html"> <link rel="import" href="chrome://resources/polymer/v1_0/paper-icon-button/paper-icon-button-light.html">
<link rel="import" href="shared_vars.html"> <link rel="import" href="../shared_vars.html">
<dom-module id="activity-log-item"> <dom-module id="activity-log-item">
<template> <template>
...@@ -34,7 +34,7 @@ ...@@ -34,7 +34,7 @@
#activity-item-main-row cr-expand-button { #activity-item-main-row cr-expand-button {
margin-inline-end: 6px; margin-inline-end: 6px;
} }
#activity-call-and-count { #activity-call-and-count {
display: flex; display: flex;
......
// Copyright 2018 The Chromium Authors. All rights reserved. // Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
...@@ -81,7 +81,7 @@ cr.define('extensions', function() { ...@@ -81,7 +81,7 @@ cr.define('extensions', function() {
e.stopPropagation(); e.stopPropagation();
this.fire( this.fire(
'delete-activity-log-item', 'delete-activity-log-item',
{activityIds: Array.from(this.data.activityIds.values())}); Array.from(this.data.activityIds.values()));
}, },
/** @private */ /** @private */
......
...@@ -29,17 +29,23 @@ ...@@ -29,17 +29,23 @@
<structure name="IDR_MD_EXTENSIONS_CODE_SECTION_JS" <structure name="IDR_MD_EXTENSIONS_CODE_SECTION_JS"
file="code_section.js" file="code_section.js"
type="chrome_html" /> type="chrome_html" />
<structure name="IDR_MD_EXTENSIONS_ACTIVITY_LOG_HTML" <structure name="IDR_MD_EXTENSIONS_ACTIVITY_LOG_ACTIVITY_LOG_HTML"
file="activity_log.html" file="activity_log/activity_log.html"
type="chrome_html" /> type="chrome_html" />
<structure name="IDR_MD_EXTENSIONS_ACTIVITY_LOG_JS" <structure name="IDR_MD_EXTENSIONS_ACTIVITY_LOG_ACTIVITY_LOG_JS"
file="activity_log.js" file="activity_log/activity_log.js"
type="chrome_html" /> type="chrome_html" />
<structure name="IDR_MD_EXTENSIONS_ACTIVITY_LOG_ITEM_HTML" <structure name="IDR_MD_EXTENSIONS_ACTIVITY_LOG_ACTIVITY_LOG_HISTORY_HTML"
file="activity_log_item.html" file="activity_log/activity_log_history.html"
type="chrome_html" /> type="chrome_html" />
<structure name="IDR_MD_EXTENSIONS_ACTIVITY_LOG_ITEM_JS" <structure name="IDR_MD_EXTENSIONS_ACTIVITY_LOG_ACTIVITY_LOG_HISTORY_JS"
file="activity_log_item.js" file="activity_log/activity_log_history.js"
type="chrome_html" />
<structure name="IDR_MD_EXTENSIONS_ACTIVITY_LOG_ACTIVITY_LOG_ITEM_HTML"
file="activity_log/activity_log_item.html"
type="chrome_html" />
<structure name="IDR_MD_EXTENSIONS_ACTIVITY_LOG_ACTIVITY_LOG_ITEM_JS"
file="activity_log/activity_log_item.js"
type="chrome_html" /> type="chrome_html" />
<structure name="IDR_MD_EXTENSIONS_DETAIL_VIEW_HTML" <structure name="IDR_MD_EXTENSIONS_DETAIL_VIEW_HTML"
file="detail_view.html" file="detail_view.html"
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
<link rel="import" href="chrome://resources/cr_elements/shared_vars_css.html"> <link rel="import" href="chrome://resources/cr_elements/shared_vars_css.html">
<link rel="import" href="chrome://resources/html/assert.html"> <link rel="import" href="chrome://resources/html/assert.html">
<link rel="import" href="chrome://resources/html/cr.html"> <link rel="import" href="chrome://resources/html/cr.html">
<link rel="import" href="activity_log.html"> <link rel="import" href="activity_log/activity_log.html">
<link rel="import" href="detail_view.html"> <link rel="import" href="detail_view.html">
<link rel="import" href="drop_overlay.html"> <link rel="import" href="drop_overlay.html">
<link rel="import" href="error_page.html"> <link rel="import" href="error_page.html">
......
...@@ -285,9 +285,11 @@ IN_PROC_BROWSER_TEST_F(ExtensionsActivityLogTest, TestActivityLogVisible) { ...@@ -285,9 +285,11 @@ IN_PROC_BROWSER_TEST_F(ExtensionsActivityLogTest, TestActivityLogVisible) {
R"(let manager = document.querySelector('extensions-manager'); R"(let manager = document.querySelector('extensions-manager');
let activityLog = let activityLog =
manager.shadowRoot.querySelector('extensions-activity-log'); manager.shadowRoot.querySelector('extensions-activity-log');
activityLog.onDataFetched.promise.then(() => { let activityLogHistory =
activityLog.shadowRoot.querySelector('activity-log-history');
activityLogHistory.whenDataFetched().then(() => {
Polymer.dom.flush(); Polymer.dom.flush();
let item = activityLog.shadowRoot.querySelector( let item = activityLogHistory.shadowRoot.querySelector(
'activity-log-item'); 'activity-log-item');
let activityKey = item.shadowRoot.getElementById('activity-key'); let activityKey = item.shadowRoot.getElementById('activity-key');
window.domAutomationController.send( window.domAutomationController.send(
......
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
/** @fileoverview Suite of tests for activity-log-history. */
suite('ExtensionsActivityLogHistoryTest', function() {
/**
* Backing extension id, same id as the one in
* extension_test_util.createExtensionInfo
* @type {string}
*/
const EXTENSION_ID = 'a'.repeat(32);
const testActivities = {
activities: [
{
activityId: '299',
activityType: 'api_call',
apiCall: 'i18n.getUILanguage',
args: 'null',
count: 10,
extensionId: EXTENSION_ID,
time: 1541203132002.664
},
{
activityId: '309',
activityType: 'dom_access',
apiCall: 'Storage.getItem',
args: 'null',
count: 35,
extensionId: EXTENSION_ID,
other: {domVerb: 'method'},
pageTitle: 'Test Extension',
pageUrl: `chrome-extension://${EXTENSION_ID}/index.html`,
time: 1541203131994.837
},
{
activityId: '301',
activityType: 'api_call',
apiCall: 'i18n.getUILanguage',
args: 'null',
count: 30,
extensionId: EXTENSION_ID,
time: 1541203172002.664
},
]
};
// Sample activities representing content script invocations. Activities with
// missing args will not be processed.
const testContentScriptActivities = {
activities: [
{
activityId: '288',
activityType: 'content_script',
apiCall: '',
args: `["script1.js","script2.js"]`,
count: 1,
extensionId: EXTENSION_ID,
pageTitle: 'Test Extension',
pageUrl: 'https://www.google.com/search'
},
{
activityId: '290',
activityType: 'content_script',
apiCall: '',
count: 1,
extensionId: EXTENSION_ID,
pageTitle: 'Test Extension',
pageUrl: 'https://www.google.com/search'
},
]
};
// Sample activities representing web requests. Activities with valid fields
// in other.webRequest should be split into multiple entries; one for every
// field. Activities with empty fields will have the group name be just the
// web request API call.
const testWebRequestActivities = {
activities: [
{
activityId: '1337',
activityType: 'web_request',
apiCall: 'webRequest.onBeforeSendHeaders',
args: 'null',
count: 300,
extensionId: EXTENSION_ID,
other: {
webRequest:
`{"modified_request_headers":true, "added_request_headers":"a"}`
},
pageUrl: `chrome-extension://${EXTENSION_ID}/index.html`,
time: 1546499283237.616
},
{
activityId: '1339',
activityType: 'web_request',
apiCall: 'webRequest.noWebRequestObject',
args: 'null',
count: 3,
extensionId: EXTENSION_ID,
other: {},
pageUrl: `chrome-extension://${EXTENSION_ID}/index.html`,
time: 1546499283237.616
},
]
};
/**
* Extension activityLogHistory created before each test.
* @type {extensions.ActivityLogHistory}
*/
let activityLogHistory;
let proxyDelegate;
let testVisible;
function setupActivityLogHistory() {
PolymerTest.clearBody();
activityLogHistory = new extensions.ActivityLogHistory();
testVisible =
extension_test_util.testVisible.bind(null, activityLogHistory);
activityLogHistory.extensionId = EXTENSION_ID;
activityLogHistory.lastSearch = '';
activityLogHistory.delegate = proxyDelegate;
document.body.appendChild(activityLogHistory);
return proxyDelegate.whenCalled('getExtensionActivityLog');
}
// Initialize an extension activity log before each test.
setup(function() {
proxyDelegate = new extensions.TestService();
});
teardown(function() {
activityLogHistory.remove();
});
test('activities are present for extension', function() {
proxyDelegate.testActivities = testActivities;
return setupActivityLogHistory().then(() => {
Polymer.dom.flush();
testVisible('#no-activities', false);
testVisible('#loading-activities', false);
testVisible('#activity-list', true);
const activityLogItems =
activityLogHistory.shadowRoot.querySelectorAll('activity-log-item');
expectEquals(activityLogItems.length, 2);
// Test the order of the activity log items here. This test is in this
// file because the logic to group activity log items by their API call
// is in activity_log.js.
expectEquals(
activityLogItems[0].$$('#activity-key').innerText,
'i18n.getUILanguage');
expectEquals(activityLogItems[0].$$('#activity-count').innerText, '40');
expectEquals(
activityLogItems[1].$$('#activity-key').innerText, 'Storage.getItem');
expectEquals(activityLogItems[1].$$('#activity-count').innerText, '35');
});
});
test('script names shown for content script activities', function() {
proxyDelegate.testActivities = testContentScriptActivities;
return setupActivityLogHistory().then(() => {
Polymer.dom.flush();
const activityLogItems =
activityLogHistory.shadowRoot.querySelectorAll('activity-log-item');
// One activity should be shown for each content script name.
expectEquals(activityLogItems.length, 2);
expectEquals(
activityLogItems[0].$$('#activity-key').innerText, 'script1.js');
expectEquals(
activityLogItems[1].$$('#activity-key').innerText, 'script2.js');
});
});
test('other.webRequest fields shown for web request activities', function() {
proxyDelegate.testActivities = testWebRequestActivities;
return setupActivityLogHistory().then(() => {
Polymer.dom.flush();
const activityLogItems =
activityLogHistory.shadowRoot.querySelectorAll('activity-log-item');
// First activity should be split into two groups as it has two actions
// recorded in the other.webRequest object. We display the names of these
// actions along with the API call. Second activity should fall back
// to using just the API call as the key. Hence we end up with three
// activity log items.
const expectedItemKeys = [
'webRequest.onBeforeSendHeaders (added_request_headers)',
'webRequest.onBeforeSendHeaders (modified_request_headers)',
'webRequest.noWebRequestObject'
];
const expectedNumItems = expectedItemKeys.length;
expectEquals(activityLogItems.length, expectedNumItems);
for (let i = 0; i < expectedNumItems; ++i) {
expectEquals(
activityLogItems[i].$$('#activity-key').innerText,
expectedItemKeys[i]);
}
});
});
test(
'clicking on the delete button for an activity row deletes that row',
function() {
proxyDelegate.testActivities = testActivities;
return setupActivityLogHistory().then(() => {
Polymer.dom.flush();
const activityLogItems =
activityLogHistory.shadowRoot.querySelectorAll(
'activity-log-item');
expectEquals(activityLogItems.length, 2);
proxyDelegate.resetResolver('getExtensionActivityLog');
activityLogItems[0].$$('#activity-delete-button').click();
// We delete the first item so we should only have one item left. This
// chaining reflects the API calls made from activity_log.js.
return proxyDelegate.whenCalled('deleteActivitiesById')
.then(() => proxyDelegate.whenCalled('getExtensionActivityLog'))
.then(() => {
Polymer.dom.flush();
expectEquals(
1,
activityLogHistory.shadowRoot
.querySelectorAll('activity-log-item')
.length);
});
});
});
test('message shown when no activities present for extension', function() {
// Spoof an API call and pretend that the extension has no activities.
proxyDelegate.testActivities = {activities: []};
return setupActivityLogHistory().then(() => {
Polymer.dom.flush();
testVisible('#no-activities', true);
testVisible('#loading-activities', false);
testVisible('#activity-list', false);
expectEquals(
activityLogHistory.shadowRoot.querySelectorAll('activity-log-item')
.length,
0);
});
});
test('message shown when activities are being fetched', function() {
// Spoof an API call and pretend that the extension has no activities.
proxyDelegate.testActivities = {activities: []};
return setupActivityLogHistory().then(() => {
// Pretend the activity log is still loading.
activityLogHistory.pageState_ = ActivityLogPageState.LOADING;
Polymer.dom.flush();
testVisible('#no-activities', false);
testVisible('#loading-activities', true);
testVisible('#activity-list', false);
});
});
});
...@@ -54,66 +54,6 @@ suite('ExtensionsActivityLogTest', function() { ...@@ -54,66 +54,6 @@ suite('ExtensionsActivityLogTest', function() {
] ]
}; };
// Sample activities representing content script invocations. Activities with
// missing args will not be processed.
const testContentScriptActivities = {
activities: [
{
activityId: '288',
activityType: 'content_script',
apiCall: '',
args: `["script1.js","script2.js"]`,
count: 1,
extensionId: EXTENSION_ID,
pageTitle: 'Test Extension',
pageUrl: 'https://www.google.com/search'
},
{
activityId: '290',
activityType: 'content_script',
apiCall: '',
count: 1,
extensionId: EXTENSION_ID,
pageTitle: 'Test Extension',
pageUrl: 'https://www.google.com/search'
},
]
};
// Sample activities representing web requests. Activities with valid fields
// in other.webRequest should be split into multiple entries; one for every
// field. Activities with empty fields will have the group name be just the
// web request API call.
const testWebRequestActivities = {
activities: [
{
activityId: '1337',
activityType: 'web_request',
apiCall: 'webRequest.onBeforeSendHeaders',
args: 'null',
count: 300,
extensionId: EXTENSION_ID,
other: {
webRequest:
`{"modified_request_headers":true, "added_request_headers":"a"}`
},
pageUrl: `chrome-extension://${EXTENSION_ID}/index.html`,
time: 1546499283237.616
},
{
activityId: '1339',
activityType: 'web_request',
apiCall: 'webRequest.noWebRequestObject',
args: 'null',
count: 3,
extensionId: EXTENSION_ID,
other: {},
pageUrl: `chrome-extension://${EXTENSION_ID}/index.html`,
time: 1546499283237.616
},
]
};
// Initialize an extension activity log before each test. // Initialize an extension activity log before each test.
setup(function() { setup(function() {
PolymerTest.clearBody(); PolymerTest.clearBody();
...@@ -127,6 +67,8 @@ suite('ExtensionsActivityLogTest', function() { ...@@ -127,6 +67,8 @@ suite('ExtensionsActivityLogTest', function() {
proxyDelegate.testActivities = testActivities; proxyDelegate.testActivities = testActivities;
document.body.appendChild(activityLog); document.body.appendChild(activityLog);
activityLog.fire('view-enter-start');
// Wait until we have finished making the call to fetch the activity log. // Wait until we have finished making the call to fetch the activity log.
return proxyDelegate.whenCalled('getExtensionActivityLog'); return proxyDelegate.whenCalled('getExtensionActivityLog');
}); });
...@@ -135,81 +77,11 @@ suite('ExtensionsActivityLogTest', function() { ...@@ -135,81 +77,11 @@ suite('ExtensionsActivityLogTest', function() {
activityLog.remove(); activityLog.remove();
}); });
test('activities are present for extension', function() {
Polymer.dom.flush();
testVisible('#no-activities', false);
testVisible('#loading-activities', false);
testVisible('#activity-list', true);
const activityLogItems =
activityLog.shadowRoot.querySelectorAll('activity-log-item');
expectEquals(activityLogItems.length, 2);
// Test the order of the activity log items here. This test is in this
// file because the logic to group activity log items by their API call
// is in activity_log.js.
expectEquals(
activityLogItems[0].$$('#activity-key').innerText,
'i18n.getUILanguage');
expectEquals(activityLogItems[0].$$('#activity-count').innerText, '40');
expectEquals(
activityLogItems[1].$$('#activity-key').innerText, 'Storage.getItem');
expectEquals(activityLogItems[1].$$('#activity-count').innerText, '35');
});
test('script names shown for content script activities', function() {
proxyDelegate.resetResolver('getExtensionActivityLog');
proxyDelegate.testActivities = testContentScriptActivities;
activityLog.refreshActivities().then(() => {
Polymer.dom.flush();
const activityLogItems =
activityLog.shadowRoot.querySelectorAll('activity-log-item');
// One activity should be shown for each content script name.
expectEquals(activityLogItems.length, 2);
expectEquals(
activityLogItems[0].$$('#activity-key').innerText, 'script1.js');
expectEquals(
activityLogItems[1].$$('#activity-key').innerText, 'script2.js');
});
});
test('other.webRequest fields shown for web request activities', function() {
proxyDelegate.resetResolver('getExtensionActivityLog');
proxyDelegate.testActivities = testWebRequestActivities;
activityLog.refreshActivities().then(() => {
Polymer.dom.flush();
const activityLogItems =
activityLog.shadowRoot.querySelectorAll('activity-log-item');
// First activity should be split into two groups as it has two actions
// recorded in the other.webRequest object. We display the names of these
// actions along with the API call. Second activity should fall back
// to using just the API call as the key. Hence we end up with three
// activity log items.
const expectedItemKeys = [
'webRequest.onBeforeSendHeaders (added_request_headers)',
'webRequest.onBeforeSendHeaders (modified_request_headers)',
'webRequest.noWebRequestObject'
];
const expectedNumItems = expectedItemKeys.length;
expectEquals(activityLogItems.length, expectedNumItems);
for (let idx = 0; idx < expectedNumItems; ++idx) {
expectEquals(
activityLogItems[idx].$$('#activity-key').innerText,
expectedItemKeys[idx]);
}
});
});
test('activities shown match search query', function() { test('activities shown match search query', function() {
const activityLogHistory = activityLog.$$('activity-log-history');
testVisible =
extension_test_util.testVisible.bind(null, activityLogHistory);
const search = activityLog.$$('cr-search-field'); const search = activityLog.$$('cr-search-field');
assertTrue(!!search); assertTrue(!!search);
...@@ -222,7 +94,8 @@ suite('ExtensionsActivityLogTest', function() { ...@@ -222,7 +94,8 @@ suite('ExtensionsActivityLogTest', function() {
Polymer.dom.flush(); Polymer.dom.flush();
const activityLogItems = const activityLogItems =
activityLog.shadowRoot.querySelectorAll('activity-log-item'); activityLogHistory.shadowRoot.querySelectorAll(
'activity-log-item');
// Since we searched for an API call, we expect only one match as // Since we searched for an API call, we expect only one match as
// activity log entries are grouped by their API call. // activity log entries are grouped by their API call.
...@@ -245,7 +118,8 @@ suite('ExtensionsActivityLogTest', function() { ...@@ -245,7 +118,8 @@ suite('ExtensionsActivityLogTest', function() {
testVisible('#activity-list', false); testVisible('#activity-list', false);
expectEquals( expectEquals(
activityLog.shadowRoot.querySelectorAll('activity-log-item') activityLogHistory.shadowRoot
.querySelectorAll('activity-log-item')
.length, .length,
0); 0);
...@@ -260,75 +134,33 @@ suite('ExtensionsActivityLogTest', function() { ...@@ -260,75 +134,33 @@ suite('ExtensionsActivityLogTest', function() {
Polymer.dom.flush(); Polymer.dom.flush();
const activityLogItems = const activityLogItems =
activityLog.shadowRoot.querySelectorAll('activity-log-item'); activityLogHistory.shadowRoot.querySelectorAll(
'activity-log-item');
expectEquals(activityLogItems.length, 2); expectEquals(activityLogItems.length, 2);
}); });
}); });
test(
'clicking on the delete button for an activity row deletes that row',
function() {
Polymer.dom.flush();
let activityLogItems =
activityLog.shadowRoot.querySelectorAll('activity-log-item');
expectEquals(activityLogItems.length, 2);
proxyDelegate.resetResolver('getExtensionActivityLog');
activityLogItems[0].$$('#activity-delete-button').click();
// We delete the first item so we should only have one item left. This
// chaining reflects the API calls made from activity_log.js.
return proxyDelegate.whenCalled('deleteActivitiesById')
.then(() => proxyDelegate.whenCalled('getExtensionActivityLog'))
.then(() => {
Polymer.dom.flush();
expectEquals(
1,
activityLog.shadowRoot.querySelectorAll('activity-log-item')
.length);
});
});
test('clicking on clear activities button clears activities', function() { test('clicking on clear activities button clears activities', function() {
activityLog.$$('#clear-activities-button').click(); activityLog.$$('#clear-activities-button').click();
return proxyDelegate.whenCalled('deleteActivitiesFromExtension') return proxyDelegate.whenCalled('deleteActivitiesFromExtension')
.then(() => { .then(() => {
Polymer.dom.flush(); Polymer.dom.flush();
const activityLogHistory = activityLog.$$('activity-log-history');
testVisible =
extension_test_util.testVisible.bind(null, activityLogHistory);
testVisible('#no-activities', true); testVisible('#no-activities', true);
testVisible('#loading-activities', false); testVisible('#loading-activities', false);
testVisible('#activity-list', false); testVisible('#activity-list', false);
expectEquals( expectEquals(
activityLog.shadowRoot.querySelectorAll('activity-log-item') activityLogHistory.shadowRoot
.querySelectorAll('activity-log-item')
.length, .length,
0); 0);
}); });
}); });
test('message shown when no activities present for extension', function() {
// Spoof an API call and pretend that the extension has no activities.
activityLog.activityData_ = [];
Polymer.dom.flush();
testVisible('#no-activities', true);
testVisible('#loading-activities', false);
testVisible('#activity-list', false);
expectEquals(
activityLog.shadowRoot.querySelectorAll('activity-log-item').length, 0);
});
test('message shown when activities are being fetched', function() {
// Pretend the activity log is still loading.
activityLog.pageState_ = ActivityLogPageState.LOADING;
Polymer.dom.flush();
testVisible('#no-activities', false);
testVisible('#loading-activities', true);
testVisible('#activity-list', false);
});
test('clicking on back button navigates to the details page', function() { test('clicking on back button navigates to the details page', function() {
Polymer.dom.flush(); Polymer.dom.flush();
......
...@@ -202,7 +202,7 @@ TEST_F('CrExtensionsItemsTest', 'HtmlInName', function() { ...@@ -202,7 +202,7 @@ TEST_F('CrExtensionsItemsTest', 'HtmlInName', function() {
CrExtensionsActivityLogTest = class extends CrExtensionsBrowserTest { CrExtensionsActivityLogTest = class extends CrExtensionsBrowserTest {
/** @override */ /** @override */
get browsePreload() { get browsePreload() {
return 'chrome://extensions/activity_log.html'; return 'chrome://extensions/activity_log/activity_log.html';
} }
get extraLibraries() { get extraLibraries() {
...@@ -216,13 +216,33 @@ TEST_F('CrExtensionsActivityLogTest', 'All', () => { ...@@ -216,13 +216,33 @@ TEST_F('CrExtensionsActivityLogTest', 'All', () => {
mocha.run(); mocha.run();
}); });
////////////////////////////////////////////////////////////////////////////////
// Extension Activity Log History Tests
CrExtensionsActivityLogHistoryTest = class extends CrExtensionsBrowserTest {
/** @override */
get browsePreload() {
return 'chrome://extensions/activity_log/activity_log_history.html';
}
get extraLibraries() {
return super.extraLibraries.concat([
'activity_log_history_test.js',
]);
}
};
TEST_F('CrExtensionsActivityLogHistoryTest', 'All', () => {
mocha.run();
});
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// Extension Activity Log Item Tests // Extension Activity Log Item Tests
CrExtensionsActivityLogItemTest = class extends CrExtensionsBrowserTest { CrExtensionsActivityLogItemTest = class extends CrExtensionsBrowserTest {
/** @override */ /** @override */
get browsePreload() { get browsePreload() {
return 'chrome://extensions/activity_log_item.html'; return 'chrome://extensions/activity_log/activity_log_item.html';
} }
get extraLibraries() { get extraLibraries() {
......
...@@ -88,6 +88,7 @@ CrElementsToastTest.* ...@@ -88,6 +88,7 @@ CrElementsToastTest.*
CrElementsToggleTest.* CrElementsToggleTest.*
CrElementsToolbarSearchFieldTest.* CrElementsToolbarSearchFieldTest.*
CrExtensionsActivityLogTest.* CrExtensionsActivityLogTest.*
CrExtensionsActivityLogHistoryTest.*
CrExtensionsActivityLogItemTest.* CrExtensionsActivityLogItemTest.*
CrExtensionsA11yTest.* CrExtensionsA11yTest.*
CrExtensionsA11yTestWithMultipleExensions.* CrExtensionsA11yTestWithMultipleExensions.*
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment