Commit 2f74f0e7 authored by Reka Norman's avatar Reka Norman Committed by Commit Bot

[App Management] Add metadata view tests.

This CL adds tests for the pin to shelf toggle in the app management
metadata view. It also creates a test util file, and refactors some
tests to use these util functions.

Bug: 906508
Change-Id: Ica7f7ceb5b097effbbab544a7f7cc066c7a9074d
Reviewed-on: https://chromium-review.googlesource.com/c/1420077Reviewed-by: default avatarcalamity <calamity@chromium.org>
Reviewed-by: default avatarKen Rockot <rockot@google.com>
Reviewed-by: default avatarEric Willigers <ericwilligers@chromium.org>
Commit-Queue: calamity <calamity@chromium.org>
Cr-Commit-Position: refs/heads/master@{#626883}
parent 566d8f2b
......@@ -117,6 +117,7 @@ if (!is_android) {
":browser_proxy",
":constants",
":fake_page_handler",
":store_client",
":types",
]
}
......@@ -125,12 +126,15 @@ if (!is_android) {
deps = [
":app_item",
":fake_page_handler",
":store_client",
]
}
js_library("permission_item") {
deps = [
":fake_page_handler",
":store_client",
":util",
]
}
......
......@@ -30,7 +30,7 @@
padding-inline-start: 24px;
}
</style>
<app-management-permission-view-header app="[[app_]]">
<app-management-permission-view-header>
</app-management-permission-view-header>
<div class="card-container">
<div id="app-info">
......
......@@ -24,25 +24,10 @@ Polymer({
},
attached: function() {
this.watch('app_', (state) => {
const selectedAppId = state.currentPage.selectedAppId;
if (selectedAppId) {
return state.apps[selectedAppId];
}
});
this.watch('app_', state => app_management.util.getSelectedApp(state));
this.updateFromStore();
},
/**
* @param {App} app
* @return {string}
* @private
*/
iconUrlFromId_: function(app) {
return app_management.util.getAppIcon(app);
},
/**
* @private
*/
......
......@@ -9,15 +9,15 @@ cr.define('app_management', function() {
class FakePageHandler {
/**
* @param {number} permissionId
* @param {Object=} config
* @param {Object=} optConfig
* @return {!Permission}
*/
static createPermission(permissionId, config) {
static createPermission(permissionId, optConfig) {
const permission = app_management.util.createPermission(
permissionId, PermissionValueType.kTriState, TriState.kBlock);
if (config) {
Object.assign(permission, config);
if (optConfig) {
Object.assign(permission, optConfig);
}
return permission;
......@@ -25,10 +25,10 @@ cr.define('app_management', function() {
/**
* @param {string} id
* @param {Object=} config
* @param {Object=} optConfig
* @return {!App}
*/
static createApp(id, config) {
static createApp(id, optConfig) {
const permissionIds = [
PwaPermissionType.CONTENT_SETTINGS_TYPE_GEOLOCATION,
PwaPermissionType.CONTENT_SETTINGS_TYPE_NOTIFICATIONS,
......@@ -46,24 +46,25 @@ cr.define('app_management', function() {
id: id,
type: apps.mojom.AppType.kWeb,
title: 'App Title',
description: '',
version: '5.1',
size: '9.0MB',
isPinned: apps.mojom.OptionalBool.kUnknown,
isPinned: apps.mojom.OptionalBool.kFalse,
permissions: permissions,
};
if (config) {
Object.assign(app, config);
if (optConfig) {
Object.assign(app, optConfig);
}
return app;
}
/**
* @param {appManagement.mojom.PageInterface} page
* @param {appManagement.mojom.PageProxy} page
*/
constructor(page) {
/** @type {appManagement.mojom.PageInterface} */
/** @type {appManagement.mojom.PageProxy} */
this.page = page;
/** @type {!Array<App>} */
......@@ -128,6 +129,31 @@ cr.define('app_management', function() {
* @param {string} appId
*/
openNativeSettings(appId) {}
/**
* @param {string} id
* @param {Object=} optConfig
*/
async addApp(id, optConfig) {
this.page.onAppAdded(FakePageHandler.createApp(id, optConfig));
await this.flushForTesting();
}
/**
* Takes an app id and an object mapping app fields to the values they
* should be changed to, and dispatches an action to carry out these
* changes.
* @param {string} id
* @param {Object} changes
*/
async changeApp(id, changes) {
this.page.onAppChanged(FakePageHandler.createApp(id, changes));
await this.flushForTesting();
}
async flushForTesting() {
await this.page.flushForTesting();
}
}
return {FakePageHandler: FakePageHandler};
......
......@@ -36,19 +36,20 @@
justify-content: space-around;
}
</style>
<template is="dom-if" if="[[pinToShelfToggleVisible_(app)]]">
<template is="dom-if" if="[[pinToShelfToggleVisible_(app_)]]">
<div id="shelf-switch-row">
<span id="shelf-switch" class="header-text">
$i18n{pinToShelf}
<cr-toggle checked="[[isPinned_(app)]]" on-change="togglePinned_">
<cr-toggle id="pin-to-shelf-toggle" checked="[[isPinned_(app_)]]"
on-change="togglePinned_">
</cr-toggle>
</span>
</div>
</template>
<div id="metadata-overview" class="secondary-text">
<span>[[versionString_(app)]]</span>
<span>[[sizeString_(app)]]</span>
<span>[[versionString_(app_)]]</span>
<span>[[sizeString_(app_)]]</span>
<!--TODO(ceciliani): Placeholder for legal declaration-->
</div>
</template>
......
......@@ -5,20 +5,29 @@
Polymer({
is: 'app-management-metadata-view',
behaviors: [
app_management.StoreClient,
],
properties: {
/** @type {App} */
app: {
app_: {
type: Object,
},
},
attached: function() {
this.watch('app_', state => app_management.util.getSelectedApp(state));
this.updateFromStore();
},
/**
* @param {App} app
* @return bool
* @private
*/
pinToShelfToggleVisible_: function(app) {
return !(app.isPinned === OptionalBool.kUnknown);
return app.isPinned !== OptionalBool.kUnknown;
},
/**
......@@ -33,11 +42,9 @@ Polymer({
},
togglePinned_: function() {
assert(this.app);
let newPinnedValue;
switch (this.app.isPinned) {
switch (this.app_.isPinned) {
case OptionalBool.kFalse:
newPinnedValue = OptionalBool.kTrue;
break;
......@@ -49,12 +56,12 @@ Polymer({
}
app_management.BrowserProxy.getInstance().handler.setPinned(
this.app.id, newPinnedValue);
this.app_.id, newPinnedValue);
},
/**
* @param {App} app
* @return {string?}
* @return {?string}
* @private
*/
versionString_: function(app) {
......@@ -67,7 +74,7 @@ Polymer({
/**
* @param {App} app
* @return {string?}
* @return {?string}
* @private
*/
sizeString_: function(app) {
......
......@@ -4,6 +4,10 @@
Polymer({
is: 'app-management-permission-item',
behaviors: [
app_management.StoreClient,
],
properties: {
/**
* The name of the permission, to be displayed to the user.
......@@ -21,14 +25,14 @@ Polymer({
/**
* @type {App}
*/
app: Object,
app_: Object,
/**
* @private {PermissionValueType}
*/
permissionValueType_: {
type: Number,
computed: 'getPermissionValueType_(app)',
computed: 'getPermissionValueType_(app_)',
},
/**
......@@ -38,13 +42,18 @@ Polymer({
*/
permissionValue_: {
type: Number,
computed: 'getPermissionValue_(app, permissionType)',
computed: 'getPermissionValue_(app_, permissionType)',
},
/** @type {string} */
icon: String,
},
attached: function() {
this.watch('app_', state => app_management.util.getSelectedApp(state));
this.updateFromStore();
},
/**
* @param {App} app
* @return {number}
......@@ -112,7 +121,7 @@ Polymer({
}
app_management.BrowserProxy.getInstance().handler.setPermission(
this.app.id, newPermission);
this.app_.id, newPermission);
},
/**
......
......@@ -43,8 +43,8 @@
<paper-ripple class="circle"></paper-ripple>
</button>
</paper-icon-button-light>
<img id="permission-view-header-icon" src="[[iconUrlFromId_(app)]]">
<div class="page-title">[[app.title]]</div>
<img id="permission-view-header-icon" src="[[iconUrlFromId_(app_)]]">
<div class="page-title">[[app_.title]]</div>
<slot name="extra-right-buttons"></slot>
<paper-button id="uninstall-button" on-click="onClickUninstallButton_">
$i18n{uninstall}
......
......@@ -10,19 +10,13 @@ Polymer({
properties: {
/** @type {App} */
app: {
app_: {
type: Object,
},
},
attached: function() {
this.watch('app', (state) => {
const selectedAppId = state.currentPage.selectedAppId;
if (selectedAppId) {
return state.apps[selectedAppId];
}
});
this.watch('app_', state => app_management.util.getSelectedApp(state));
this.updateFromStore();
},
......@@ -50,6 +44,6 @@ Polymer({
* @private
*/
onClickUninstallButton_: function() {
app_management.BrowserProxy.getInstance().handler.uninstall(this.app.id);
app_management.BrowserProxy.getInstance().handler.uninstall(this.app_.id);
},
});
......@@ -59,7 +59,7 @@
margin-inline-end: 16px;
}
</style>
<app-management-permission-view-header app="[[app_]]">
<app-management-permission-view-header>
<div slot="extra-right-buttons" id="extra-button">
<paper-button id="site-settings-button"
class="secondary-text"
......@@ -76,7 +76,6 @@
<div id="permission-list" class="card-container">
<app-management-permission-item
class="permission-card-row separated-row header-text"
app="[[app_]]"
permission-label="$i18n{notifications}"
permission-type="CONTENT_SETTINGS_TYPE_NOTIFICATIONS">
</app-management-permission-item>
......@@ -100,21 +99,18 @@
<iron-collapse opened="[[listExpanded_]]">
<app-management-permission-item
class="subpermission-row"
app="[[app_]]"
icon="cr:location-on"
permission-label="$i18n{location}"
permission-type="CONTENT_SETTINGS_TYPE_GEOLOCATION">
</app-management-permission-item>
<app-management-permission-item
class="subpermission-row"
app="[[app_]]"
icon="cr:videocam"
permission-label="$i18n{camera}"
permission-type="CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA">
</app-management-permission-item>
<app-management-permission-item
class="subpermission-row"
app="[[app_]]"
icon="cr:mic"
permission-label="$i18n{microphone}"
permission-type="CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC">
......
......@@ -24,13 +24,7 @@ Polymer({
},
attached: function() {
this.watch('app_', (state) => {
const selectedAppId = state.currentPage.selectedAppId;
if (selectedAppId) {
return state.apps[selectedAppId];
}
});
this.watch('app_', state => app_management.util.getSelectedApp(state));
this.updateFromStore();
},
......
......@@ -18,6 +18,8 @@ cr.define('app_management', function() {
* @return {AppMap}
*/
AppState.addApp = function(apps, action) {
assert(!apps[action.app.id]);
const newAppEntry = {};
newAppEntry[action.app.id] = action.app;
return Object.assign({}, apps, newAppEntry);
......
......@@ -56,10 +56,24 @@ cr.define('app_management.util', function() {
return `chrome://extension-icon/${app.id}/128/1`;
}
/**
* If there is no selected app, returns undefined so that Polymer bindings
* won't be updated.
* @param {AppManagementPageState} state
* @return {App|undefined}
*/
function getSelectedApp(state) {
const selectedAppId = state.currentPage.selectedAppId;
if (selectedAppId) {
return state.apps[selectedAppId];
}
}
return {
createEmptyState: createEmptyState,
createInitialState: createInitialState,
createPermission: createPermission,
getAppIcon: getAppIcon,
getSelectedApp: getSelectedApp,
};
});
......@@ -18,7 +18,9 @@ AppManagementBrowserTest.prototype = {
browsePreload: 'chrome://apps',
extraLibraries: PolymerTest.getLibraries(ROOT_PATH),
extraLibraries: PolymerTest.getLibraries(ROOT_PATH).concat([
'test_util.js',
]),
featureList: ['features::kAppManagement', ''],
......@@ -26,7 +28,6 @@ AppManagementBrowserTest.prototype = {
runAccessibilityChecks: true,
};
function AppManagementAppTest() {}
AppManagementAppTest.prototype = {
......@@ -55,6 +56,20 @@ TEST_F('AppManagementMainViewTest', 'All', function() {
mocha.run();
});
function AppManagementMetadataViewTest() {}
AppManagementMetadataViewTest.prototype = {
__proto__: AppManagementBrowserTest.prototype,
extraLibraries: AppManagementBrowserTest.prototype.extraLibraries.concat([
'metadata_view_test.js',
]),
};
TEST_F('AppManagementMetadataViewTest', 'All', function() {
mocha.run();
});
function AppManagementReducersTest() {}
AppManagementReducersTest.prototype = {
......
......@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
'use strict';
suite('<app-management-app>', function() {
test('loads', async function() {
// Check that the browser responds to the getApps() message.
......
......@@ -2,74 +2,57 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
'use strict';
suite('<app-management-main-view>', function() {
let mainView;
let fakeHandler;
let callbackRouterProxy;
let appIdCounter;
/**
* @param {number} numApps
*/
async function addApps(numApps) {
for (let i = 0; i < numApps; i++) {
await fakeHandler.addApp((appIdCounter++).toString());
}
}
setup(function() {
appIdCounter = 0;
mainView = document.createElement('app-management-main-view');
PolymerTest.clearBody();
let browserProxy = app_management.BrowserProxy.getInstance();
callbackRouterProxy = browserProxy.callbackRouter.createProxy();
fakeHandler = new app_management.FakePageHandler(callbackRouterProxy);
browserProxy.handler = fakeHandler;
app_management.Store.instance_ = new app_management.Store();
app_management.Store.getInstance().init(
app_management.util.createEmptyState());
fakeHandler = setupFakeHandler();
replaceStore();
document.body.appendChild(mainView);
});
/**
* @param {number} numApps
* @return {!Array<appManagement.mojom.App>} apps
*/
function createTestApps(numApps) {
let apps = [];
for (let i = 0; i < numApps; i++) {
apps.push(
app_management.FakePageHandler.createApp('TestApp' + appIdCounter++));
}
return apps;
}
async function addApps(apps) {
for (const app of apps) {
callbackRouterProxy.onAppAdded(app);
}
await callbackRouterProxy.flushForTesting();
}
test('simple app addition', async function() {
// Ensure there is no apps initially
expectEquals(
0, mainView.root.querySelectorAll('app-management-app-item').length);
let apps = createTestApps(1);
await addApps(apps);
const appId = '1';
await fakeHandler.addApp(appId);
let appItems = mainView.root.querySelectorAll('app-management-app-item');
expectEquals(1, appItems.length);
expectEquals(appItems[0].app.id, apps[0].id);
expectEquals(appId, appItems[0].app.id);
});
test('more apps bar visibility', async function() {
// The more apps bar shouldn't appear when there are 4 apps.
await addApps(createTestApps(4));
await addApps(4);
expectEquals(
4, mainView.root.querySelectorAll('app-management-app-item').length);
expectTrue(mainView.$['expander-row'].hidden);
// The more apps bar appears when there are 5 apps.
await addApps(createTestApps(1));
await addApps(1);
expectEquals(
5, mainView.root.querySelectorAll('app-management-app-item').length);
expectFalse(mainView.$['expander-row'].hidden);
......@@ -77,14 +60,14 @@ suite('<app-management-main-view>', function() {
test('notifications sublabel collapsibility', async function() {
// The three spans contains collapsible attribute.
await addApps(createTestApps(4));
await addApps(4);
const pieces = await mainView.getNotificationSublabelPieces_();
expectTrue(pieces.filter(p => p.arg === '$1')[0].collapsible);
expectTrue(pieces.filter(p => p.arg === '$2')[0].collapsible);
expectTrue(pieces.filter(p => p.arg === '$3')[0].collapsible);
// Checking ",and other x apps" is non-collapsible
await addApps(createTestApps(6));
await addApps(6);
const pieces2 = await mainView.getNotificationSublabelPieces_();
expectFalse(pieces2.filter(p => p.arg === '$4')[0].collapsible);
});
......
// Copyright 2018 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.
'use strict';
suite('<app-management-metadata-view>', function() {
let metadataView;
let fakeHandler;
const APP_ID = '1';
setup(async function() {
metadataView = document.createElement('app-management-metadata-view');
PolymerTest.clearBody();
fakeHandler = setupFakeHandler();
replaceStore();
// Add an app, and make it the currently selected app.
await fakeHandler.addApp(APP_ID);
app_management.Store.getInstance().dispatch(
app_management.actions.changePage(PageType.DETAIL, APP_ID));
document.body.appendChild(metadataView);
});
test(
'when app.isPinned is unknown, the pin to shelf toggle is not visible',
async function() {
await fakeHandler.changeApp(APP_ID, {isPinned: OptionalBool.kUnknown});
// Check that the toggle is not visible.
const toggle = metadataView.root.getElementById('pin-to-shelf-toggle');
if (toggle) {
expectTrue(isHidden(toggle));
}
});
test(
'clicking the pin to shelf toggle changes the isPinned field of the app',
async function() {
// Set app.isPinned to false.
await fakeHandler.changeApp(APP_ID, {isPinned: OptionalBool.kFalse});
const toggle = metadataView.root.getElementById('pin-to-shelf-toggle');
// Check that the toggle is visible and is not checked.
expectTrue(!!toggle && !isHidden(toggle));
expectFalse(toggle.checked);
// Toggle from false to true.
toggle.click();
await fakeHandler.flushForTesting();
// Check that the isPinned field of the app has changed.
expectEquals(OptionalBool.kTrue, metadataView.app_.isPinned);
// Check that the toggle is now checked.
expectTrue(toggle.checked);
// Toggle from true to false.
toggle.click();
await fakeHandler.flushForTesting();
// Check that the isPinned field of the app has changed.
expectEquals(OptionalBool.kFalse, metadataView.app_.isPinned);
// Check that the toggle is no longer checked.
expectFalse(toggle.checked);
});
});
......@@ -2,9 +2,10 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
'use strict';
suite('app state', function() {
let apps;
let action;
let state;
function createApp(id, config) {
......@@ -27,8 +28,7 @@ suite('app state', function() {
test('updates when an app is added', function() {
const newApp = createApp('3', {type: 1, title: 'a'});
action = app_management.actions.addApp(newApp);
const action = app_management.actions.addApp(newApp);
apps = app_management.AppState.updateApps(apps, action);
// Check that apps contains a key for each app id.
......@@ -45,7 +45,7 @@ suite('app state', function() {
test('updates when an app is changed', function() {
const changedApp = createApp('2', {type: 1, title: 'a'});
action = app_management.actions.changeApp(changedApp);
const action = app_management.actions.changeApp(changedApp);
apps = app_management.AppState.updateApps(apps, action);
// Check that app has changed.
......@@ -58,7 +58,7 @@ suite('app state', function() {
});
test('updates when an app is removed', function() {
action = app_management.actions.removeApp('1');
const action = app_management.actions.removeApp('1');
apps = app_management.AppState.updateApps(apps, action);
// Check that app is removed.
......@@ -74,7 +74,7 @@ suite('app state', function() {
state.currentPage.selectedAppId = '1';
state.currentPage.pageType = PageType.DETAIL;
action = app_management.actions.removeApp('1');
let action = app_management.actions.removeApp('1');
state = app_management.reduceAction(state, action);
assertEquals(null, state.currentPage.selectedAppId);
......@@ -97,7 +97,7 @@ suite('app state', function() {
state.currentPage.selectedAppId = '1';
state.currentPage.pageType = PageType.DETAIL;
action = app_management.actions.changePage(PageType.MAIN);
let action = app_management.actions.changePage(PageType.MAIN);
state = app_management.reduceAction(state, action);
assertEquals(null, state.currentPage.selectedAppId);
......@@ -113,7 +113,7 @@ suite('app state', function() {
test('state updates when changing to app detail page', function() {
// State updates when a valid app detail page is selected.
action = app_management.actions.changePage(PageType.DETAIL, '2');
let action = app_management.actions.changePage(PageType.DETAIL, '2');
state = app_management.reduceAction(state, action);
assertEquals('2', state.currentPage.selectedAppId);
......@@ -131,7 +131,7 @@ suite('app state', function() {
});
test('state updates when changing to notifications page', function() {
action = app_management.actions.changePage(PageType.NOTIFICATIONS);
const action = app_management.actions.changePage(PageType.NOTIFICATIONS);
state = app_management.reduceAction(state, action);
assertEquals(PageType.NOTIFICATIONS, state.currentPage.pageType);
......
// Copyright 2018 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.
'use strict';
/**
* @return {app_management.FakePageHandler}
*/
function setupFakeHandler() {
const browserProxy = app_management.BrowserProxy.getInstance();
const callbackRouterProxy = browserProxy.callbackRouter.createProxy();
const fakeHandler = new app_management.FakePageHandler(callbackRouterProxy);
browserProxy.handler = fakeHandler;
return fakeHandler;
}
/**
* Replace the app management store instance with a new, empty store.
*/
function replaceStore() {
app_management.Store.instance_ = new app_management.Store();
app_management.Store.getInstance().init(
app_management.util.createEmptyState());
}
/**
* @param {Element} element
* @return {bool}
*/
function isHidden(element) {
const rect = element.getBoundingClientRect();
return rect.height === 0 && rect.width === 0;
}
......@@ -54,6 +54,11 @@ goog.provide('{{module.namespace}}.{{interface.name}}CallbackRouter');
* @return {!{{module.namespace}}.{{interface.name}}Request}
*/
createRequest() {}
/**
* @return {Promise}
*/
flushForTesting() {}
};
{{module.namespace}}.{{interface.name}} = class {
......
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