Commit 89925c61 authored by Nancy Wang's avatar Nancy Wang

Revert "NTP: Allow modules to instantiate the header"

This reverts commit 2cf475dd.

Reason for revert: <INSERT REASONING HERE>
This CL looks break browser tests:
https://ci.chromium.org/p/chromium/builders/ci/Win7%20Tests%20%28dbg%29%281%29

2 Test Suite(s) failed. browser_tests failed because of:
NewTabPageCustomizeDialogTest.All
ProfilePickerEnterpriseCreationFlowBrowserTest.CreateSignedInProfile
NewTabPageModulesDummyModuleTest.All
NewTabPageModulesModuleWrapperTest.All
...17 more failure(s) (21 total)...
interactive_ui_tests failed because of:
NewTabPageDoodleShareDialogFocusTest.All
NewTabPageCustomizeDialogFocusTest.All
NewTabPageMostVisitedFocusTest.All

Original change's description:
> NTP: Allow modules to instantiate the header
>
> This CL removes the module header from the wrapper and instead allows
> the modules to instantiate it instead. This makes the title and actions
> properties of the ModuleDescriptor obsolete, so this CL also removes
> those.
>
> Bug: 1152205, 1152216
> Change-Id: Ib524a59c8c8a7cc69e130b31a8557b903233ab95
> Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2570390
> Reviewed-by: Tibor Goldschwendt <tiborg@chromium.org>
> Commit-Queue: Tommy Steimel <steimel@chromium.org>
> Cr-Commit-Position: refs/heads/master@{#834424}

TBR=tiborg@chromium.org,steimel@chromium.org,chromium-scoped@luci-project-accounts.iam.gserviceaccount.com

Change-Id: I2a3471f3bef9d8b6b1c3c7c6f026f0d1c9da00d2
No-Presubmit: true
No-Tree-Checks: true
No-Try: true
Bug: 1152205
Bug: 1152216
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2578296Reviewed-by: default avatarNancy Wang <nancylingwang@chromium.org>
Cr-Commit-Position: refs/heads/master@{#834505}
parent cd8ea6f1
......@@ -349,7 +349,7 @@
<dom-if if="[[lazyRender_]]" restamp>
<template>
<cr-toast id="dismissModuleToast" duration="10000">
<div id="dismissModuleToastMessage">[[dismissedModuleData_.message]]</div>
<div id="dismissModuleToastMessage">[[dismissModuleToastMessage_]]</div>
<cr-button id="undoDismissModuleButton"
aria-label="$i18n{undoDescription}"
on-click="onUndoDismissModuleButtonClick_">
......
......@@ -248,15 +248,21 @@ class AppElement extends PolymerElement {
moduleDescriptors_: Object,
/**
* Data about the most recently dismissed module.
* @type {?{id: string, element: !Element, message: string,
* restoreCallback: function()}}
* The <ntp-module-wrapper> element of the last dismissed module.
* @type {?Element}
* @private
*/
dismissedModuleData_: {
dismissedModuleWrapper_: {
type: Object,
value: null,
},
/**
* The message shown in the toast when a module is dismissed.
* @type {string}
* @private
*/
dismissModuleToastMessage_: String,
};
}
......@@ -879,25 +885,19 @@ class AppElement extends PolymerElement {
}
/**
* @param {!CustomEvent<{message: string, restoreCallback: function()}>} e
* Event notifying a module was dismissed. Contains the message to show in
* the toast.
* @param {!CustomEvent<string>} e Event notifying a module was dismissed.
* Contains the message to show in the toast.
* @private
*/
onDismissModule_(e) {
this.dismissedModuleData_ = {
id: $$(this, '#modules').itemForElement(e.target).id,
element: /** @type {!Element} */ (e.target),
message: loadTimeData.getStringF(
'dismissModuleToastMessage', e.detail.message),
restoreCallback: e.detail.restoreCallback,
};
this.dismissedModuleData_.element.hidden = true;
this.dismissedModuleWrapper_ = /** @type {!Element} */ (e.target);
// Notify the user.
this.dismissModuleToastMessage_ = e.detail;
$$(this, '#dismissModuleToast').show();
// Notify the backend.
this.pageHandler_.onDismissModule(this.dismissedModuleData_.id);
this.pageHandler_.onDismissModule(
this.dismissedModuleWrapper_.descriptor.id);
}
/**
......@@ -905,15 +905,14 @@ class AppElement extends PolymerElement {
*/
onUndoDismissModuleButtonClick_() {
// Restore the module.
this.dismissedModuleData_.restoreCallback();
this.dismissedModuleData_.element.hidden = false;
this.dismissedModuleWrapper_.restore();
// Notify the user.
$$(this, '#dismissModuleToast').hide();
// Notify the backend.
this.pageHandler_.onRestoreModule(this.dismissedModuleData_.id);
this.pageHandler_.onRestoreModule(
this.dismissedModuleWrapper_.descriptor.id);
this.dismissedModuleData_ = null;
this.dismissedModuleWrapper_ = null;
}
/**
......
......@@ -29,12 +29,6 @@ js_library("module_wrapper") {
]
}
js_library("module_header") {
deps = [
"//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
]
}
js_library("module_registry") {
deps = [
":module_descriptor",
......@@ -43,10 +37,7 @@ js_library("module_registry") {
}
html_to_js("web_components_local") {
js_files = [
"module_header.js",
"module_wrapper.js",
]
js_files = [ "module_wrapper.js" ]
}
group("web_components") {
......@@ -85,7 +76,6 @@ if (optimize_webui) {
in_folder = target_gen_dir
out_folder = "$target_gen_dir/$preprocess_folder"
in_files = [
"module_header.js",
"module_wrapper.js",
"dummy/module.js",
"task_module/module.js",
......
<style>
:host {
width: 100%;
}
cr-grid {
--cr-grid-gap: 8px;
}
#moduleContent {
align-items: center;
display: flex;
height: 260px;
justify-content: center;
}
.tile-item {
color: var(--cr-primary-text-color);
height: 120px;
......@@ -24,14 +13,11 @@
border-radius: 8px;
}
</style>
<ntp-module-header module-id="[[id]]">[[title]]</ntp-module-header>
<div id="moduleContent">
<cr-grid id="tiles" columns="3">
<template id="tileList" is="dom-repeat" items="[[tiles]]">
<div class="tile-item" title="[[item.label]]">
<img is="ntp-img" auto-src="[[item.imageUrl]]"></img>
<span>[[item.value]]</span>
</div>
</template>
</cr-grid>
</div>
<cr-grid id="tiles" columns="3">
<template id="tileList" is="dom-repeat" items="[[tiles]]">
<div class="tile-item" title="[[item.label]]">
<img is="ntp-img" auto-src="[[item.imageUrl]]"></img>
<span>[[item.value]]</span>
</div>
</template>
</cr-grid>
......@@ -6,7 +6,6 @@ import 'chrome://resources/cr_elements/cr_grid/cr_grid.js';
import '../../img.js';
import '../../strings.m.js';
import '../module_header.js';
import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js';
import {html, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
......@@ -30,14 +29,8 @@ class DummyModuleElement extends PolymerElement {
static get properties() {
return {
/** @type {!string} */
id: String,
/** @type {!Array<!foo.mojom.FooDataItem>} */
tiles: Array,
/** @type {!string} */
title: String,
};
}
......@@ -55,26 +48,18 @@ class DummyModuleElement extends PolymerElement {
customElements.define(DummyModuleElement.is, DummyModuleElement);
/**
* @param {!string} id
* @param {!string} titleId
* @return {!DummyModuleElement}
*/
function createDummyElement(id, titleId) {
const element = new DummyModuleElement();
element.id = id;
element.title = loadTimeData.getString(titleId);
return element;
}
/** @type {!ModuleDescriptor} */
export const dummyDescriptor = new ModuleDescriptor(
/*id=*/ 'dummy',
/*heightPx=*/ 314,
() => Promise.resolve(createDummyElement('dummy', 'modulesDummyTitle')));
/*heightPx=*/ 260, () => Promise.resolve({
element: new DummyModuleElement(),
title: loadTimeData.getString('modulesDummyTitle'),
}));
/** @type {!ModuleDescriptor} */
export const dummyDescriptor2 = new ModuleDescriptor(
/*id=*/ 'dummy2',
/*heightPx=*/ 314,
() => Promise.resolve(createDummyElement('dummy2', 'modulesDummy2Title')));
/*heightPx=*/ 260, () => Promise.resolve({
element: new DummyModuleElement(),
title: loadTimeData.getString('modulesDummy2Title'),
}));
......@@ -3,6 +3,9 @@
// found in the LICENSE file.
/**
* @returns {Promise<?HTMLElement>}
* @returns {Promise<?{
* element: !HTMLElement,
* title: string,
* }>}
*/
window.loadKaleidoscopeModule = () => {};
......@@ -13,7 +13,7 @@ import {ModuleDescriptor} from '../module_descriptor.js';
/** @type {!ModuleDescriptor} */
export const kaleidoscopeDescriptor = new ModuleDescriptor(
/*id=*/ 'kaleidoscope',
/*heightPx=*/ 384,
/*heightPx=*/ 330,
async () => {
return loadKaleidoscopeModule();
},
......
......@@ -10,7 +10,20 @@ import {BrowserProxy} from '../browser_proxy.js';
*/
/**
* @typedef {function(): !Promise<?HTMLElement>}
* @typedef {{
* info: (function()|undefined),
* dismiss: (function():string|undefined),
* restore: (function()|undefined),
* }}
*/
let Actions;
/**
* @typedef {function(): !Promise<?{
* element: !HTMLElement,
* title: string,
* actions: (undefined|Actions),
* }>}
*/
let InitializeModuleCallback;
......@@ -25,10 +38,14 @@ export class ModuleDescriptor {
this.id_ = id;
/** @private {number} */
this.heightPx_ = heightPx;
/** @private {?string} */
this.title_ = null;
/** @private {HTMLElement} */
this.element_ = null;
/** @private {!InitializeModuleCallback} */
this.initializeCallback_ = initializeCallback;
/** @private {?Actions} */
this.actions_ = null;
}
/** @return {string} */
......@@ -41,16 +58,29 @@ export class ModuleDescriptor {
return this.heightPx_;
}
/** @return {?string} */
get title() {
return this.title_;
}
/** @return {?HTMLElement} */
get element() {
return this.element_;
}
/** @return {?Actions} */
get actions() {
return this.actions_;
}
async initialize() {
this.element_ = await this.initializeCallback_();
if (!this.element_) {
const info = await this.initializeCallback_();
if (!info) {
return;
}
this.title_ = info.title;
this.element_ = info.element;
this.actions_ = info.actions || null;
BrowserProxy.getInstance().handler.onModuleLoaded(
this.id_, BrowserProxy.getInstance().now());
}
......
<style include="cr-icons">
:host {
align-items: center;
display: flex;
height: 22px;
margin: 16px;
}
#title {
color: var(--cr-primary-text-color);
font-size: 15px;
}
#headerSpacer {
flex-grow: 1;
}
#infoButton {
--cr-icon-image: url(./icons/info.svg);
}
#dismissButton {
--cr-icon-button-margin-start: 4px;
}
</style>
<span id="title"><slot></slot></span>
<div id="headerSpacer"></div>
<template is="dom-if" if="[[showInfoButton]]">
<cr-icon-button id="infoButton" title="$i18n{moduleInfoButtonTitle}"
on-click="onInfoButtonClick_">
</cr-icon-button>
</template>
<template is="dom-if" if="[[showDismissButton]]">
<cr-icon-button id="dismissButton" title="$i18n{moduleDismissButtonTitle}"
class="icon-clear" on-click="onDismissButtonClick_">
</cr-icon-button>
</template>
// Copyright 2020 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 {assert} from 'chrome://resources/js/assert.m.js';
import {html, microTask, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
import {BrowserProxy} from '../browser_proxy.js';
/** @fileoverview Element that displays a header inside a module. */
class ModuleHeaderElement extends PolymerElement {
static get is() {
return 'ntp-module-header';
}
static get template() {
return html`{__html_template__}`;
}
static get properties() {
return {
/**
* The ID of the module.
* @type {!string}
*/
moduleId: String,
/**
* The title to be displayed.
* @type {!string}
*/
title: String,
/**
* True if the header should display an info button.
* @type {boolean}
*/
showInfoButton: {
type: Boolean,
value: false,
},
/**
* True if the header should display a dismiss button.
* @type {boolean}
*/
showDismissButton: {
type: Boolean,
value: false,
},
};
}
ready() {
super.ready();
const observer = new IntersectionObserver(([{intersectionRatio}]) => {
if (intersectionRatio >= .5) {
observer.disconnect();
BrowserProxy.getInstance().handler.onModuleImpression(
this.moduleId, BrowserProxy.getInstance().now());
}
}, {threshold: .5});
// Calling observe will immediately invoke the callback. If the header is
// fully shown when the page loads, the first callback invocation will
// happen before the header has dimensions. For this reason, we start
// observing after the element has had a chance to be rendered.
microTask.run(() => {
observer.observe(this);
});
}
/** @private */
onInfoButtonClick_() {
this.dispatchEvent(new CustomEvent('info-button-click', {bubbles: true}));
}
/** @private */
onDismissButtonClick_() {
this.dispatchEvent(
new CustomEvent('dismiss-button-click', {bubbles: true}));
}
}
customElements.define(ModuleHeaderElement.is, ModuleHeaderElement);
<style>
<style include="cr-icons">
:host {
background-color: var(--ntp-background-override-color);
border: solid var(--ntp-border-color) 1px;
......@@ -8,10 +8,48 @@
overflow: hidden;
}
#header {
align-items: center;
display: flex;
height: 22px;
margin: 16px;
}
#title {
color: var(--cr-primary-text-color);
font-size: 15px;
}
#headerSpacer {
flex-grow: 1;
}
#infoButton {
--cr-icon-image: url(./icons/info.svg);
}
#dismissButton {
--cr-icon-button-margin-start: 4px;
}
#moduleElement {
align-items: center;
display: flex;
justify-content: center;
}
</style>
<div id="header">
<span id="title">[[descriptor.title]]</span>
<div id="headerSpacer"></div>
<template is="dom-if" if="[[descriptor.actions.info]]">
<cr-icon-button id="infoButton" title="$i18n{moduleInfoButtonTitle}"
on-click="onInfoButtonClick_">
</cr-icon-button>
</template>
<template is="dom-if" if="[[descriptor.actions.dismiss]]">
<cr-icon-button id="dismissButton" title="$i18n{moduleDismissButtonTitle}"
class="icon-clear" on-click="onDismissButtonClick_">
</cr-icon-button>
</template>
</div>
<div id="moduleElement"></div>
......@@ -3,7 +3,7 @@
// found in the LICENSE file.
import {assert} from 'chrome://resources/js/assert.m.js';
import {html, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
import {html, microTask, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
import {BrowserProxy} from '../browser_proxy.js';
import {ModuleDescriptor} from './module_descriptor.js';
......@@ -38,13 +38,49 @@ class ModuleWrapperElement extends PolymerElement {
assert(!oldValue);
this.$.moduleElement.appendChild(this.descriptor.element);
this.$.moduleElement.style.height = `${this.descriptor.heightPx}px`;
const observer = new IntersectionObserver(([{intersectionRatio}]) => {
if (intersectionRatio >= .5) {
observer.disconnect();
BrowserProxy.getInstance().handler.onModuleImpression(
this.descriptor.id, BrowserProxy.getInstance().now());
}
}, {threshold: .5});
// Calling observe will immediately invoke the callback. If the header is
// fully shown when the page loads, the first callback invocation will
// happen before the header has dimensions. For this reason, we start
// observing after the element has had a chance to be rendered.
microTask.run(() => {
observer.observe(this.$.header);
});
// Log at most one usage per module per NTP page load. This is possible,
// if a user opens a link in a new tab.
this.descriptor.element.addEventListener('usage', () => {
BrowserProxy.getInstance().handler.onModuleUsage(this.descriptor.id);
}, {once: true});
}
/** @private */
onInfoButtonClick_() {
this.descriptor.actions.info();
}
/** @private */
onDismissButtonClick_() {
this.hidden = true;
const message = this.descriptor.actions.dismiss();
this.dispatchEvent(new CustomEvent('dismiss-module', {
bubbles: true,
composed: true,
detail: message,
}));
}
restore() {
this.hidden = false;
if (this.descriptor.actions.restore) {
this.descriptor.actions.restore();
}
}
}
customElements.define(ModuleWrapperElement.is, ModuleWrapperElement);
<style include="cr-hidden-style">
:host {
display: flex;
flex-direction: column;
height: 100%;
width: 100%;
}
#moduleContent {
box-sizing: border-box;
display: block;
flex-grow: 1;
height: 100%;
padding-inline-end: 15px;
padding-inline-start: 15px;
width: 100%;
......@@ -177,38 +170,31 @@
text-decoration: none;
}
</style>
<ntp-module-header module-id="[[id]]"
show-info-button on-info-button-click="onInfoButtonClick_"
show-dismiss-button on-dismiss-button-click="onDismissButtonClick_">
[[task.title]]
</ntp-module-header>
<div id="moduleContent">
<div id="taskItems">
<template is="dom-repeat" id="taskItemsRepeat" items="[[task.taskItems]]"
on-dom-change="onDomChange_">
<a class="task-item" href="[[item.targetUrl.url]]"
on-click="onTaskItemClick_" on-auxclick="onTaskItemClick_">
<div class="image-background">
<div class="image-container">
<img is="ntp-img" auto-src="[[item.imageUrl.url]]"></img>
</div>
<div id="taskItems">
<template is="dom-repeat" id="taskItemsRepeat" items="[[task.taskItems]]"
on-dom-change="onDomChange_">
<a class="task-item" href="[[item.targetUrl.url]]"
on-click="onTaskItemClick_" on-auxclick="onTaskItemClick_">
<div class="image-background">
<div class="image-container">
<img is="ntp-img" auto-src="[[item.imageUrl.url]]"></img>
</div>
<div class="price" hidden$="[[!item.price]]">[[item.price]]</div>
<div class="name" title="[[item.name]]">[[item.name]]</div>
<div class="info">[[item.info]]</div>
</a>
</template>
</div>
<div id="relatedSearches">
<template is="dom-repeat" id="relatedSearchesRepeat"
items="[[task.relatedSearches]]" on-dom-change="onDomChange_">
<a class="pill" href="[[item.targetUrl.url]]" on-click="onPillClick_"
on-auxclick="onPillClick_">
<div class="loupe"></div>
<div class="search-text">[[item.text]]</div>
</a>
</template>
</div>
</div>
<div class="price" hidden$="[[!item.price]]">[[item.price]]</div>
<div class="name" title="[[item.name]]">[[item.name]]</div>
<div class="info">[[item.info]]</div>
</a>
</template>
</div>
<div id="relatedSearches">
<template is="dom-repeat" id="relatedSearchesRepeat"
items="[[task.relatedSearches]]" on-dom-change="onDomChange_">
<a class="pill" href="[[item.targetUrl.url]]" on-click="onPillClick_"
on-auxclick="onPillClick_">
<div class="loupe"></div>
<div class="search-text">[[item.text]]</div>
</a>
</template>
</div>
<template is="dom-if" if="[[showInfoDialog]]" restamp>
<cr-dialog show-on-attach>
......
......@@ -3,9 +3,9 @@
// found in the LICENSE file.
import '../../img.js';
import '../module_header.js';
import 'chrome://resources/cr_elements/hidden_style_css.m.js';
import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js';
import {html, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
import {ModuleDescriptor} from '../module_descriptor.js';
import {TaskModuleHandlerProxy} from './task_module_handler_proxy.js';
......@@ -27,9 +27,6 @@ class TaskModuleElement extends PolymerElement {
static get properties() {
return {
/** @type {string} */
id: String,
/** @type {!taskModule.mojom.TaskModuleType} */
taskModuleType: {
type: Number,
......@@ -84,36 +81,11 @@ class TaskModuleElement extends PolymerElement {
this.dispatchEvent(new Event('usage', {bubbles: true, composed: true}));
}
/** @private */
onInfoButtonClick_() {
this.showInfoDialog = true;
}
/** @private */
onCloseClick_() {
this.showInfoDialog = false;
}
/** @private */
onDismissButtonClick_() {
TaskModuleHandlerProxy.getInstance().handler.dismissTask(
this.taskModuleType, this.task.name);
this.dispatchEvent(new CustomEvent('dismiss-module', {
bubbles: true,
composed: true,
detail: {
message: this.task.name,
restoreCallback: this.onRestore_.bind(this),
},
}));
}
/** @private */
onRestore_() {
TaskModuleHandlerProxy.getInstance().handler.restoreTask(
this.taskModuleType, this.task.name);
}
/** @private */
onDomChange_() {
if (!this.intersectionObserver_) {
......@@ -134,8 +106,8 @@ class TaskModuleElement extends PolymerElement {
customElements.define(TaskModuleElement.is, TaskModuleElement);
/** @return {!Promise<?HTMLElement>} */
async function createModule(id, taskModuleType) {
/** @return {!Promise<?{element: !HTMLElement, title: string}>} */
async function createModule(taskModuleType) {
const {task} =
await TaskModuleHandlerProxy.getInstance().handler.getPrimaryTask(
taskModuleType);
......@@ -145,20 +117,34 @@ async function createModule(id, taskModuleType) {
const element = new TaskModuleElement();
element.taskModuleType = taskModuleType;
element.task = task;
element.id = id;
return element;
return {
element: element,
title: task.title,
actions: {
info: () => {
element.showInfoDialog = true;
},
dismiss: () => {
TaskModuleHandlerProxy.getInstance().handler.dismissTask(
taskModuleType, task.name);
return loadTimeData.getStringF('dismissModuleToastMessage', task.name);
},
restore: () => {
TaskModuleHandlerProxy.getInstance().handler.restoreTask(
taskModuleType, task.name);
},
},
};
}
/** @type {!ModuleDescriptor} */
export const recipeTasksDescriptor = new ModuleDescriptor(
/*id=*/ 'recipe_tasks',
/*heightPx=*/ 260,
createModule.bind(
null, 'recipe_tasks', taskModule.mojom.TaskModuleType.kRecipe));
/*heightPx=*/ 206,
createModule.bind(null, taskModule.mojom.TaskModuleType.kRecipe));
/** @type {!ModuleDescriptor} */
export const shoppingTasksDescriptor = new ModuleDescriptor(
/*id=*/ 'shopping_tasks',
/*heightPx=*/ 324,
createModule.bind(
null, 'shopping_tasks', taskModule.mojom.TaskModuleType.kShopping));
/*heightPx=*/ 270,
createModule.bind(null, taskModule.mojom.TaskModuleType.kShopping));
......@@ -480,10 +480,12 @@ suite('NewTabPageAppTest', () => {
{
id: 'foo',
element: document.createElement('div'),
title: 'Foo Title',
},
{
id: 'bar',
element: document.createElement('div'),
title: 'Bar Title',
}
]);
$$(app, 'ntp-middle-slot-promo')
......@@ -505,43 +507,45 @@ suite('NewTabPageAppTest', () => {
test('modules can be dismissed and restored', async () => {
// Arrange.
let dismissCalled = false;
let restoreCalled = false;
const moduleElement = document.createElement('div');
// Act.
moduleResolver.resolve([{
id: 'foo',
element: moduleElement,
element: document.createElement('div'),
title: 'Foo Title',
actions: {
dismiss: () => {
dismissCalled = true;
return 'Foo was removed';
},
restore: () => {
restoreCalled = true;
},
}
}]);
await flushTasks(); // Wait for module descriptor resolution.
// Assert.
const modules = app.shadowRoot.querySelectorAll('ntp-module-wrapper');
assertEquals(1, modules.length);
assertNotStyle($$(modules[0], '#dismissButton'), 'display', 'none');
assertFalse($$(app, '#dismissModuleToast').open);
// Act.
moduleElement.dispatchEvent(new CustomEvent('dismiss-module', {
bubbles: true,
composed: true,
detail: {
message: 'Foo',
restoreCallback: _ => {
restoreCalled = true;
},
},
}));
$$(modules[0], '#dismissButton').click();
await flushTasks();
// Assert.
assertTrue($$(app, '#dismissModuleToast').open);
assertEquals(
'Removed Foo',
'Foo was removed',
$$(app, '#dismissModuleToastMessage').textContent.trim());
assertNotStyle($$(app, '#undoDismissModuleButton'), 'display', 'none');
assertTrue(dismissCalled);
assertEquals(
'foo', await testProxy.handler.whenCalled('onDismissModule'));
assertFalse(restoreCalled);
// Act.
$$(app, '#undoDismissModuleButton').click();
......
......@@ -12,7 +12,10 @@ suite('NewTabPageModulesModuleRegistryTest', () => {
const bazModule = document.createElement('div');
const bazModuleResolver = new PromiseResolver();
ModuleRegistry.getInstance().registerModules([
new ModuleDescriptor('foo', 100, () => Promise.resolve(fooModule)),
new ModuleDescriptor('foo', 100, () => Promise.resolve({
element: fooModule,
title: 'Foo Title',
})),
new ModuleDescriptor('bar', 200, () => null),
new ModuleDescriptor('baz', 300, () => bazModuleResolver.promise),
]);
......@@ -20,16 +23,21 @@ suite('NewTabPageModulesModuleRegistryTest', () => {
// Act.
const modulesPromise = ModuleRegistry.getInstance().initializeModules();
// Delayed promise resolution to test async module instantiation.
bazModuleResolver.resolve(bazModule);
bazModuleResolver.resolve({
element: bazModule,
title: 'Baz Title',
});
const modules = await modulesPromise;
// Assert.
assertEquals(2, modules.length);
assertEquals('foo', modules[0].id);
assertEquals(100, modules[0].heightPx);
assertEquals('Foo Title', modules[0].title);
assertDeepEquals(fooModule, modules[0].element);
assertEquals('baz', modules[1].id);
assertEquals(300, modules[1].heightPx);
assertEquals('Baz Title', modules[1].title);
assertDeepEquals(bazModule, modules[1].element);
});
});
......@@ -22,10 +22,12 @@ suite('NewTabPageModulesModuleWrapperTest', () => {
moduleWrapper.descriptor = {
id: 'foo',
heightPx: 100,
title: 'Foo Title',
element: moduleElement,
};
// Assert.
assertEquals('Foo Title', moduleWrapper.$.title.textContent);
assertEquals(100, $$(moduleWrapper, '#moduleElement').offsetHeight);
assertDeepEquals(
moduleElement, $$(moduleWrapper, '#moduleElement').children[0]);
......@@ -36,12 +38,14 @@ suite('NewTabPageModulesModuleWrapperTest', () => {
moduleWrapper.descriptor = {
id: 'foo',
heightPx: 100,
title: 'Foo Title',
element: moduleElement,
};
assertThrows(() => {
moduleWrapper.descriptor = {
id: 'foo',
heightPx: 100,
title: 'Foo Title',
element: moduleElement,
};
});
......
......@@ -4,7 +4,7 @@
import {shoppingTasksDescriptor, TaskModuleHandlerProxy} from 'chrome://new-tab-page/new_tab_page.js';
import {TestBrowserProxy} from 'chrome://test/test_browser_proxy.m.js';
import {eventToPromise, flushTasks} from 'chrome://test/test_util.m.js';
import {eventToPromise} from 'chrome://test/test_util.m.js';
suite('NewTabPageModulesTaskModuleTest', () => {
/**
......@@ -180,30 +180,25 @@ suite('NewTabPageModulesTaskModuleTest', () => {
};
testProxy.handler.setResultFor('getPrimaryTask', Promise.resolve({task}));
// Arrange.
// Act.
await shoppingTasksDescriptor.initialize();
const moduleElement = shoppingTasksDescriptor.element;
document.body.append(moduleElement);
await flushTasks();
// Assert.
assertEquals('function', typeof shoppingTasksDescriptor.actions.dismiss);
assertEquals('function', typeof shoppingTasksDescriptor.actions.restore);
// Act.
const waitForDismissEvent = eventToPromise('dismiss-module', moduleElement);
const dismissButton =
moduleElement.shadowRoot.querySelector('ntp-module-header')
.shadowRoot.querySelector('#dismissButton');
dismissButton.click();
const dismissEvent = await waitForDismissEvent;
const toastMessage = dismissEvent.detail.message;
const restoreCallback = dismissEvent.detail.restoreCallback;
const toastMessage = shoppingTasksDescriptor.actions.dismiss();
// Assert.
assertEquals('Hello world', toastMessage);
assertEquals('Removed Hello world', toastMessage);
assertDeepEquals(
[taskModule.mojom.TaskModuleType.kShopping, 'Hello world'],
await testProxy.handler.whenCalled('dismissTask'));
// Act.
restoreCallback();
shoppingTasksDescriptor.actions.restore();
// Assert.
assertDeepEquals(
......
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