chromium/chrome/test/data/webui/chromeos/personalization_app/wallpaper_fullscreen_element_test.ts

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

/** @fileoverview Test suite for wallpaper-fullscreen component.  */

import 'chrome://personalization/strings.m.js';

import {CurrentWallpaper, DailyRefreshType, DisplayableImage, FullscreenPreviewState, GooglePhotosPhoto, OnlineImageType, setFullscreenStateAction, setShouldWaitForFullscreenOpacityTransitionsForTesting, WallpaperActionName, WallpaperFullscreenElement, WallpaperImage, WallpaperLayout, WallpaperObserver, WallpaperType} from 'chrome://personalization/js/personalization_app.js';
import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
import {stringToMojoString16} from 'chrome://resources/js/mojo_type_util.js';
import {assertDeepEquals, assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
import {flushTasks, waitAfterNextRender} from 'chrome://webui-test/polymer_test_util.js';

import {baseSetup, initElement} from './personalization_app_test_utils.js';
import {TestPersonalizationStore} from './test_personalization_store.js';
import {TestWallpaperProvider} from './test_wallpaper_interface_provider.js';

suite('WallpaperFullscreenElementTest', function() {
  let wallpaperFullscreenElement: WallpaperFullscreenElement|null = null;
  let wallpaperProvider: TestWallpaperProvider;
  let personalizationStore: TestPersonalizationStore;

  const currentSelectedCustomImage: CurrentWallpaper = {
    descriptionContent: '',
    descriptionTitle: '',
    key: 'testing',
    layout: WallpaperLayout.kCenter,
    type: WallpaperType.kCustomized,
  };

  const pendingSelectedCustomImage:
      DisplayableImage = {path: '/local/image/path.jpg'};

  setup(() => {
    setShouldWaitForFullscreenOpacityTransitionsForTesting(false);
    const mocks = baseSetup();
    wallpaperProvider = mocks.wallpaperProvider;
    wallpaperProvider.isInTabletModeResponse = true;
    personalizationStore = mocks.personalizationStore;
  });

  teardown(async () => {
    if (wallpaperFullscreenElement) {
      wallpaperFullscreenElement.remove();
    }
    wallpaperFullscreenElement = null;
    await flushTasks();
  });

  function mockFullscreenApis() {
    const container =
        wallpaperFullscreenElement!.shadowRoot!.getElementById('container');

    let fullscreenElement: HTMLElement|null = null;

    const requestFullscreenPromise = new Promise<void>((resolve) => {
      container!.requestFullscreen = () => {
        fullscreenElement = container;
        container!.dispatchEvent(new Event('fullscreenchange'));
        resolve();
        return requestFullscreenPromise;
      };
    });

    wallpaperFullscreenElement!.getFullscreenElement = () => fullscreenElement;
    const exitFullscreenPromise = new Promise<void>((resolve) => {
      wallpaperFullscreenElement!.exitFullscreen = () => {
        assertTrue(!!fullscreenElement);
        fullscreenElement = null;
        container!.dispatchEvent(new Event('fullscreenchange'));
        resolve();
        return exitFullscreenPromise;
      };
    });

    return {requestFullscreenPromise, exitFullscreenPromise};
  }

  test('toggles element visibility on full screen change', async () => {
    wallpaperFullscreenElement = initElement(WallpaperFullscreenElement);
    const {requestFullscreenPromise, exitFullscreenPromise} =
        mockFullscreenApis();
    await waitAfterNextRender(wallpaperFullscreenElement);

    const container =
        wallpaperFullscreenElement.shadowRoot!.getElementById('container');
    assertTrue(container!.hidden, 'container hidden at first');

    personalizationStore.data.wallpaper.fullscreen =
        FullscreenPreviewState.LOADING;
    personalizationStore.data.wallpaper.currentSelected =
        currentSelectedCustomImage;
    personalizationStore.notifyObservers();

    await requestFullscreenPromise;

    assertTrue(
        container!.hidden, 'container still hidden while in loading state');

    personalizationStore.data.wallpaper.fullscreen =
        FullscreenPreviewState.VISIBLE;
    personalizationStore.notifyObservers();
    await waitAfterNextRender(wallpaperFullscreenElement);

    assertFalse(container!.hidden, 'container now visible');

    personalizationStore.data.wallpaper.fullscreen = FullscreenPreviewState.OFF;
    personalizationStore.notifyObservers();

    await exitFullscreenPromise;

    assertTrue(container!.hidden);
  });

  test('sets default layout option on when entering preview', async () => {
    wallpaperFullscreenElement = initElement(WallpaperFullscreenElement);
    mockFullscreenApis();
    await waitAfterNextRender(wallpaperFullscreenElement);

    assertEquals(
        null, wallpaperFullscreenElement['selectedLayout_'],
        'wallpaper layout starts null');

    personalizationStore.data.wallpaper.fullscreen =
        FullscreenPreviewState.VISIBLE;
    personalizationStore.data.wallpaper.currentSelected =
        currentSelectedCustomImage;
    personalizationStore.notifyObservers();

    assertEquals(
        WallpaperLayout.kCenterCropped,
        wallpaperFullscreenElement['selectedLayout_'],
        'wallpaper layout set to center cropped');

    personalizationStore.data.wallpaper.fullscreen = FullscreenPreviewState.OFF;
    personalizationStore.notifyObservers();
    await waitAfterNextRender(wallpaperFullscreenElement);

    assertEquals(
        null, wallpaperFullscreenElement['selectedLayout_'],
        'wallpaper layout set back to null');
  });

  test('sets fullscreen classes on body when entering fullscreen', async () => {
    const fullscreenClassName = 'fullscreen-preview';
    const fullscreenTransitionClassName = 'fullscreen-preview-transition';
    wallpaperFullscreenElement = initElement(WallpaperFullscreenElement);
    const {requestFullscreenPromise} = mockFullscreenApis();
    await waitAfterNextRender(wallpaperFullscreenElement);

    assertFalse(
        document.body.classList.contains(fullscreenClassName),
        `${fullscreenClassName} is not set`);
    assertFalse(
        document.body.classList.contains(fullscreenTransitionClassName),
        `${fullscreenTransitionClassName} is not set`);

    personalizationStore.data.wallpaper.fullscreen =
        FullscreenPreviewState.LOADING;
    personalizationStore.data.wallpaper.currentSelected =
        currentSelectedCustomImage;
    personalizationStore.notifyObservers();

    await requestFullscreenPromise;

    assertFalse(
        document.body.classList.contains(fullscreenClassName),
        `${fullscreenClassName} is not set during loading`);
    assertFalse(
        document.body.classList.contains(fullscreenTransitionClassName),
        `${fullscreenTransitionClassName} is not set during loading`);

    personalizationStore.data.wallpaper.fullscreen =
        FullscreenPreviewState.VISIBLE;
    personalizationStore.notifyObservers();
    await waitAfterNextRender(wallpaperFullscreenElement);

    assertTrue(
        document.body.classList.contains(fullscreenClassName),
        `${fullscreenClassName} is set for visible`);
    assertTrue(
        document.body.classList.contains(fullscreenTransitionClassName),
        `${fullscreenTransitionClassName} is set for visible`);

    personalizationStore.data.wallpaper.fullscreen = FullscreenPreviewState.OFF;
    personalizationStore.notifyObservers();
    await waitAfterNextRender(wallpaperFullscreenElement);

    assertFalse(
        document.body.classList.contains(fullscreenClassName),
        `${fullscreenClassName} is removed`);
    assertFalse(
        document.body.classList.contains(fullscreenTransitionClassName),
        `${fullscreenTransitionClassName} is removed`);
  });

  test('exits full screen on exit button click', async () => {
    wallpaperFullscreenElement = initElement(WallpaperFullscreenElement);
    const {requestFullscreenPromise} = mockFullscreenApis();
    await waitAfterNextRender(wallpaperFullscreenElement);

    personalizationStore.data.wallpaper.fullscreen =
        FullscreenPreviewState.LOADING;
    personalizationStore.data.wallpaper.currentSelected =
        currentSelectedCustomImage;
    personalizationStore.notifyObservers();

    await requestFullscreenPromise;

    personalizationStore.data.wallpaper.fullscreen =
        FullscreenPreviewState.VISIBLE;
    personalizationStore.notifyObservers();

    personalizationStore.expectAction(WallpaperActionName.SET_FULLSCREEN_STATE);

    const exitButton =
        wallpaperFullscreenElement.shadowRoot!.getElementById('exit');
    exitButton!.click();

    const action = await personalizationStore.waitForAction(
        WallpaperActionName.SET_FULLSCREEN_STATE);
    assertDeepEquals(
        setFullscreenStateAction(FullscreenPreviewState.OFF), action);
  });

  [{
    pendingSelectedImage: pendingSelectedCustomImage,
    shouldShow: true,
  },
   {
     pendingSelectedImage: {
       url: {url: ''},
       attribution: [],
       assetId: 0n,
       unitId: 0n,
       type: OnlineImageType.kUnknown,
     } as WallpaperImage,
     shouldShow: false,
   },
   {
     pendingSelectedImage: {
       id: 'test_id',
       name: 'asdf',
       date: stringToMojoString16('February'),
       url: {url: ''},
     } as GooglePhotosPhoto,
     shouldShow: true,
   },
  ]
      .forEach(
          testCase => test(
              'shows layout options for custom and Google Photos images',
              async () => {
                wallpaperFullscreenElement =
                    initElement(WallpaperFullscreenElement);
                await waitAfterNextRender(wallpaperFullscreenElement);

                assertEquals(
                    null,
                    wallpaperFullscreenElement.shadowRoot!.getElementById(
                        'layoutButtons'));

                // Select a wallpaper in preview mode from a starting state
                // where the layout buttons have not been created.
                personalizationStore.data.wallpaper.pendingSelected =
                    testCase.pendingSelectedImage;
                personalizationStore.notifyObservers();
                await waitAfterNextRender(wallpaperFullscreenElement);

                // Verify whether layout buttons are created.
                let layoutButtonsEl =
                    wallpaperFullscreenElement.shadowRoot!.getElementById(
                        'layoutButtons');
                assertEquals(!!layoutButtonsEl, testCase.shouldShow);

                // Select a custom wallpaper to make sure that layout buttons
                // are shown.
                personalizationStore.data.wallpaper.pendingSelected =
                    pendingSelectedCustomImage;
                personalizationStore.notifyObservers();
                await waitAfterNextRender(wallpaperFullscreenElement);

                layoutButtonsEl =
                    wallpaperFullscreenElement.shadowRoot!.getElementById(
                        'layoutButtons');
                assertTrue(!!layoutButtonsEl);

                // Select a wallpaper in preview mode from a starting state
                // where the layout buttons have been created.
                personalizationStore.data.wallpaper.pendingSelected =
                    testCase.pendingSelectedImage;
                personalizationStore.notifyObservers();
                await waitAfterNextRender(wallpaperFullscreenElement);

                // The layout buttons will still exist from having been added to
                // the shadow DOM already, so now we test whether they are
                // actually showing.
                layoutButtonsEl =
                    wallpaperFullscreenElement.shadowRoot!.getElementById(
                        'layoutButtons');
                assertTrue(!!layoutButtonsEl);
                assertEquals(
                    getComputedStyle(layoutButtonsEl).display,
                    testCase.shouldShow ? 'grid' : 'none');
              }));

  test('clicking layout option selects image with new layout', async () => {
    wallpaperFullscreenElement = initElement(WallpaperFullscreenElement);
    const {requestFullscreenPromise} = mockFullscreenApis();
    await waitAfterNextRender(wallpaperFullscreenElement);

    personalizationStore.data.wallpaper.currentSelected =
        currentSelectedCustomImage;
    personalizationStore.data.wallpaper.pendingSelected =
        pendingSelectedCustomImage;
    personalizationStore.data.wallpaper.fullscreen =
        FullscreenPreviewState.LOADING;
    personalizationStore.notifyObservers();

    await requestFullscreenPromise;

    personalizationStore.data.wallpaper.fullscreen =
        FullscreenPreviewState.VISIBLE;
    personalizationStore.notifyObservers();

    let button = wallpaperFullscreenElement.shadowRoot!.querySelector(
                     'cr-button[data-layout="FILL"]')! as HTMLButtonElement;
    button.click();

    const [fillImage, fillLayout, fillPreviewMode] =
        await wallpaperProvider.whenCalled('selectLocalImage');
    wallpaperProvider.reset();

    assertEquals(pendingSelectedCustomImage, fillImage);
    assertEquals(WallpaperLayout.kCenterCropped, fillLayout);
    assertTrue(fillPreviewMode);

    button = wallpaperFullscreenElement.shadowRoot!.querySelector(
                 'cr-button[data-layout="CENTER"]') as HTMLButtonElement;
    button.click();

    const [centerImage, centerLayout, centerPreviewMode] =
        await wallpaperProvider.whenCalled('selectLocalImage');

    assertEquals(pendingSelectedCustomImage, centerImage);
    assertEquals(WallpaperLayout.kCenter, centerLayout);
    assertTrue(centerPreviewMode);
  });

  test('aria pressed set for chosen layout option', async () => {
    wallpaperFullscreenElement = initElement(WallpaperFullscreenElement);
    const {requestFullscreenPromise} = mockFullscreenApis();
    await waitAfterNextRender(wallpaperFullscreenElement);

    personalizationStore.data.wallpaper.currentSelected =
        currentSelectedCustomImage;
    personalizationStore.data.wallpaper.pendingSelected =
        pendingSelectedCustomImage;
    personalizationStore.data.wallpaper.fullscreen =
        FullscreenPreviewState.LOADING;
    personalizationStore.notifyObservers();

    await requestFullscreenPromise;

    personalizationStore.data.wallpaper.fullscreen =
        FullscreenPreviewState.VISIBLE;
    personalizationStore.notifyObservers();

    // Current image is kCenter but initial state should reset to default.
    assertEquals(
        WallpaperLayout.kCenter,
        personalizationStore.data.wallpaper.currentSelected.layout);

    const center = wallpaperFullscreenElement.shadowRoot!.querySelector(
        'cr-button[data-layout="CENTER"]');
    const fill = wallpaperFullscreenElement.shadowRoot!.querySelector(
        'cr-button[data-layout="FILL"]');

    assertEquals('false', center!.getAttribute('aria-pressed'));
    assertEquals('true', fill!.getAttribute('aria-pressed'));

    wallpaperFullscreenElement['selectedLayout_'] = WallpaperLayout.kCenter;
    await waitAfterNextRender(wallpaperFullscreenElement);

    assertEquals('true', center!.getAttribute('aria-pressed'));
    assertEquals('false', fill!.getAttribute('aria-pressed'));
  });

  test('clicking set as wallpaper confirms wallpaper', async () => {
    wallpaperFullscreenElement = initElement(WallpaperFullscreenElement);
    mockFullscreenApis();
    await waitAfterNextRender(wallpaperFullscreenElement);

    personalizationStore.data.wallpaper.fullscreen =
        FullscreenPreviewState.LOADING;
    personalizationStore.data.wallpaper.currentSelected = {
      ...wallpaperProvider.currentWallpaper,
      type: WallpaperType.kDaily,
    };
    personalizationStore.data.wallpaper.dailyRefresh = {
      id: wallpaperProvider.collections![0]!.id,
      type: DailyRefreshType.BACKDROP,
    };
    personalizationStore.data.wallpaper.pendingSelected =
        wallpaperProvider.images![1]!;
    personalizationStore.notifyObservers();

    await waitAfterNextRender(wallpaperFullscreenElement);

    personalizationStore.data.wallpaper.fullscreen =
        FullscreenPreviewState.VISIBLE;
    personalizationStore.notifyObservers();

    await waitAfterNextRender(wallpaperFullscreenElement);

    const setAsWallpaperButton =
        wallpaperFullscreenElement.shadowRoot!.getElementById('confirm') as
        HTMLButtonElement;
    setAsWallpaperButton.click();

    await wallpaperProvider.whenCalled('confirmPreviewWallpaper');
  });

  test('sets aria label on cr-button', async () => {
    wallpaperFullscreenElement = initElement(WallpaperFullscreenElement);
    mockFullscreenApis();
    await waitAfterNextRender(wallpaperFullscreenElement);

    assertEquals(
        loadTimeData.getString('ariaLabelExitFullscreen'),
        wallpaperFullscreenElement.shadowRoot
            ?.querySelector('.fullscreen-button')
            ?.getAttribute('aria-label'),
        'exit button aria label is set');
  });

  test('exits fullscreen on popstate', async () => {
    WallpaperObserver.initWallpaperObserverIfNeeded();
    wallpaperFullscreenElement = initElement(WallpaperFullscreenElement);
    const {requestFullscreenPromise, exitFullscreenPromise} =
        mockFullscreenApis();
    await waitAfterNextRender(wallpaperFullscreenElement);

    // Add a history entry to pop later.
    window.history.pushState(null, '', window.location.href + '#test');

    personalizationStore.data.wallpaper.fullscreen =
        FullscreenPreviewState.LOADING;
    personalizationStore.notifyObservers();

    await requestFullscreenPromise;

    personalizationStore.data.wallpaper.fullscreen =
        FullscreenPreviewState.VISIBLE;
    personalizationStore.notifyObservers();

    personalizationStore.setReducersEnabled(true);
    window.history.back();

    // Triggered by popstate.
    await wallpaperProvider.whenCalled('cancelPreviewWallpaper');

    // Simulate the response from wallpaper controller.
    wallpaperProvider.wallpaperObserverRemote!.onWallpaperChanged(
        wallpaperProvider.currentWallpaper);
    wallpaperProvider.wallpaperObserverRemote!.onWallpaperPreviewEnded();
    // Second |onWallpaperChanged| is generated by
    // |personalization_app_wallpaper_provider_impl.cc|.
    wallpaperProvider.wallpaperObserverRemote!.onWallpaperChanged(
        wallpaperProvider.currentWallpaper);

    // |exitFullscreenPromise| from wallpaper preview being canceled.
    await exitFullscreenPromise;
    WallpaperObserver.shutdown();
  });
});