// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import {getTrustedHTML} from 'chrome://resources/js/static_types.js';
import {assertEquals, assertNotEquals} from 'chrome://webui-test/chromeos/chai_assert.js';
import {waitUntil} from '../common/js/test_error_reporting.js';
import {waitForElementUpdate} from '../common/js/unittest_util.js';
import {XfCloudPanel} from './xf_cloud_panel.js';
// Creates new <xf-cloud-panel> for each test.
export function setUp() {
document.body.innerHTML = getTrustedHTML`
<xf-cloud-panel></xf-cloud-panel>
`;
}
// Returns the <xf-cloud-panel> element.
async function getCloudPanel(): Promise<XfCloudPanel> {
const panel = document.querySelector<XfCloudPanel>('xf-cloud-panel')!;
assertNotEquals(null, panel);
assertEquals('XF-CLOUD-PANEL', panel.tagName);
await waitForElementUpdate(panel);
return panel;
}
// Checks that a computed style equals `want`.
function checkStyle(element: HTMLElement, tag: string, want: string) {
const style = element.computedStyleMap().get(tag)!;
assertNotEquals(null, style);
assertEquals(want, style.toString());
}
// The different types of selectors that appear.
enum PanelSelector {
PREPARING = '#preparing-state',
PROGRESSING = '#progress-state',
FINISHED = '#progress-finished',
OFFLINE = '#progress-offline',
NOT_ENOUGH_SPACE = '#progress-not-enough-space',
METERED = '#progress-metered-network',
}
// Checks that a panel type is visible and that all the other types are hidden.
function checkVisiblePanel(panel: XfCloudPanel, selector: PanelSelector) {
const progressStateElement =
panel.shadowRoot!.querySelector<HTMLDivElement>('#progress-state')!;
const progressFinishedElement =
panel.shadowRoot!.querySelector<HTMLDivElement>('#progress-finished')!;
const progressOfflineElement =
panel.shadowRoot!.querySelector<HTMLDivElement>('#progress-offline')!;
const progressNotEnoughSpaceElement =
panel.shadowRoot!.querySelector<HTMLDivElement>(
'#progress-not-enough-space')!;
const progressPreparingElement =
panel.shadowRoot!.querySelector<HTMLDivElement>('#progress-preparing')!;
const progressMeteredNetworkElement =
panel.shadowRoot!.querySelector<HTMLDivElement>(
'#progress-metered-network')!;
// Some stages use flexbox to center or vertically align their items, others
// use a normal block display.
const displayValue = (type: PanelSelector, success: string = 'block') => {
return (type === selector) ? success : 'none';
};
checkStyle(
progressStateElement, 'display', displayValue(PanelSelector.PROGRESSING));
checkStyle(
progressFinishedElement, 'display',
displayValue(PanelSelector.FINISHED, 'flex'));
checkStyle(
progressOfflineElement, 'display',
displayValue(PanelSelector.OFFLINE, 'flex'));
checkStyle(
progressNotEnoughSpaceElement, 'display',
displayValue(PanelSelector.NOT_ENOUGH_SPACE, 'flex'));
checkStyle(
progressPreparingElement, 'display',
displayValue(PanelSelector.PREPARING, 'flex'));
checkStyle(
progressMeteredNetworkElement, 'display',
displayValue(PanelSelector.METERED, 'flex'));
}
// Tests that the initial `<xf-cloud-panel>` element defaults to the preparing
// state until both items and percentage are set.
export async function testInitialElementIsInPreparingState() {
const panel = await getCloudPanel();
// Expect neither `items` nor `progress` to be set on `<xf-cloud-panel>`.
assertEquals(panel.getAttribute('items'), null);
assertEquals(panel.getAttribute('percentage'), null);
// When no items or percentage is set on the element, it should show in a
// preparing state.
checkVisiblePanel(panel, PanelSelector.PREPARING);
}
// Tests that when updating the progress values, it updates the underlying
// progress bar element.
export async function testProgressStateUpdatesProgressBar() {
const panel = await getCloudPanel();
// The initial progress state should default to preparing.
assertEquals(panel.getAttribute('items'), null);
assertEquals(panel.getAttribute('percentage'), null);
checkVisiblePanel(panel, PanelSelector.PREPARING);
// Update the items and progress
panel.setAttribute('items', '3');
panel.setAttribute('percentage', '12');
// Wait for the progress bar to update and the #progress-state div to show.
await waitForElementUpdate(panel);
checkVisiblePanel(panel, PanelSelector.PROGRESSING);
const progress =
panel.shadowRoot!.querySelector<HTMLProgressElement>('progress')!;
assertEquals('12', progress.getAttribute('value'));
}
// Tests that when clicking the "Google Drive settings" button an event is
// emitted.
export async function testWhenGoogleDriveSettingsIsClickedEventIsEmitted() {
const panel = await getCloudPanel();
// Set up an event listener for the button to be clicked.
let clicks = 0;
panel.addEventListener(
XfCloudPanel.events.DRIVE_SETTINGS_CLICKED, () => ++clicks);
// Click the "Google Drive settings" button.
const settingsButton =
panel.shadowRoot!.querySelector<HTMLButtonElement>('button.action')!;
settingsButton.click();
// Wait until the number of clicks has incremented.
await waitUntil(() => clicks === 1);
}
// Tests that when percentage is 100% it should show the "All files synced"
// state and not the progress state.
export async function testWhenPercentage100OnlyDoneStateShows() {
const panel = await getCloudPanel();
// When no attributes have been set, should default to preparing.
checkVisiblePanel(panel, PanelSelector.PREPARING);
// Update the items to 3 and total percentage to 50%.
panel.setAttribute('items', '3');
panel.setAttribute('percentage', '50');
// Ensure the progressStateElement is showing but the finished element is not.
checkVisiblePanel(panel, PanelSelector.PROGRESSING);
// Update the total percentage to 100%.
panel.setAttribute('percentage', '100');
// Ensure the progressState is not showing but the finished element is
// showing.
checkVisiblePanel(panel, PanelSelector.FINISHED);
}
// Tests that when the offline type attribute is supplied, the other states
// should all be hidden.
export async function testWhenOfflineTypeAttributeInUseOtherStatesHidden() {
const panel = await getCloudPanel();
// When no attributes have been set, should default to preparing.
checkVisiblePanel(panel, PanelSelector.PREPARING);
// Update the items to 3 and total percentage to 50%.
panel.setAttribute('items', '3');
panel.setAttribute('percentage', '50');
// Ensure only the in progress element is visible.
checkVisiblePanel(panel, PanelSelector.PROGRESSING);
// Update the type to be offline.
panel.setAttribute('type', 'offline');
// Ensure the only visible div is the offline one.
checkVisiblePanel(panel, PanelSelector.OFFLINE);
}
// Tests that when the not_enough_space type attribute is supplied, the other
// states should all be hidden.
export async function
testWhenNotEnoughSpaceTypeAttributeInUseOtherStatesHidden() {
const panel = await getCloudPanel();
// When no attributes have been set, should default to preparing.
checkVisiblePanel(panel, PanelSelector.PREPARING);
// Update the items to 3 and total percentage to 50%.
panel.setAttribute('items', '3');
panel.setAttribute('percentage', '50');
// Ensure only the in progress element is visible.
checkVisiblePanel(panel, PanelSelector.PROGRESSING);
// Update the type to be not_enough_space.
panel.setAttribute('type', 'not_enough_space');
// Ensure the only visible div is the not_enough_space one.
checkVisiblePanel(panel, PanelSelector.NOT_ENOUGH_SPACE);
}
// Tests that only accepted cloud panel types are valid values for the `type`
// attribute.
export async function testOnlyAcceptedTypesUpdateTypeProperty() {
const panel = await getCloudPanel();
// The `type` attribute should initially be undefined.
assertEquals(panel.type, undefined);
// Setting it to a valid value should update the underlying type.
panel.setAttribute('type', 'not_enough_space');
checkVisiblePanel(panel, PanelSelector.NOT_ENOUGH_SPACE);
// Setting it to some random value will update the HTML elements type
// attribute but the actual elements `type` property will get set to null as
// it is not an acceptable value.
panel.setAttribute('type', 'non-existant-type');
assertEquals('non-existant-type', panel.getAttribute('type'));
assertEquals(panel.type, null);
}
// Tests that when percentage is 0, the progress is shown instead of preparing.
export async function testVariousCombinationsOfAttributes() {
const panel = await getCloudPanel();
// Setting the items to 1 but no percentage should show the preparing state.
panel.setAttribute('items', '1');
checkVisiblePanel(panel, PanelSelector.PREPARING);
// Only setting the percentage attribute should stay in preparing.
panel.removeAttribute('items');
panel.setAttribute('percentage', '0');
assertEquals('0', panel.getAttribute('percentage'));
checkVisiblePanel(panel, PanelSelector.PREPARING);
// When percentage is 0 and items is 1, the preparing should disappear and the
// progress should show.
panel.setAttribute('items', '1');
panel.setAttribute('percentage', '0');
checkVisiblePanel(panel, PanelSelector.PROGRESSING);
// When no items are set but the percentage is 100, the panel should be
// finished.
panel.setAttribute('items', '0');
panel.setAttribute('percentage', '100');
checkVisiblePanel(panel, PanelSelector.FINISHED);
// The type attribute should take precedence over progressing.
panel.setAttribute('type', 'offline');
checkVisiblePanel(panel, PanelSelector.OFFLINE);
}
// Tests that metered network properly updates the state.
export async function testMeteredNetworkState() {
const panel = await getCloudPanel();
// Setting it to a valid value should update the underlying type.
panel.setAttribute('type', 'metered_network');
checkVisiblePanel(panel, PanelSelector.METERED);
}