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

// 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 {beginLoadRecentSeaPenImagesAction, beginLoadSelectedImageAction, beginLoadSelectedRecentSeaPenImageAction, beginSearchSeaPenThumbnailsAction, beginSelectRecentSeaPenImageAction, beginSelectSeaPenThumbnailAction, endSelectRecentSeaPenImageAction, endSelectSeaPenThumbnailAction, FullscreenPreviewState, getRecentSeaPenImageIds, getSeaPenStore, getSeaPenThumbnails, SeaPenState, SeaPenStoreAdapter, SeaPenStoreInterface, selectRecentSeaPenImage, selectSeaPenThumbnail, setCurrentSeaPenQueryAction, setFullscreenStateAction, setRecentSeaPenImagesAction, setSeaPenFullscreenStateAction, setSeaPenThumbnailsAction, setSelectedRecentSeaPenImageAction, setThumbnailResponseStatusCodeAction, WallpaperLayout, WallpaperType} from 'chrome://personalization/js/personalization_app.js';
import {MantaStatusCode} from 'chrome://resources/ash/common/sea_pen/sea_pen.mojom-webui.js';
import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
import {assertDeepEquals, assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';

import {filterAndFlattenState, typeCheck} from './personalization_app_test_utils.js';
import {TestPersonalizationStore} from './test_personalization_store.js';
import {TestSeaPenProvider} from './test_sea_pen_interface_provider.js';

suite('SeaPen reducers', () => {
  let seaPenProvider: TestSeaPenProvider;
  let personalizationStore: TestPersonalizationStore;
  let seaPenStore: SeaPenStoreInterface;

  setup(() => {
    loadTimeData.overrideValues({isSeaPenEnabled: true});
    loadTimeData.overrideValues({isSeaPenTextInputEnabled: true});
    seaPenProvider = new TestSeaPenProvider();
    personalizationStore = new TestPersonalizationStore({});
    personalizationStore.setReducersEnabled(true);
    personalizationStore.replaceSingleton();
    SeaPenStoreAdapter.initSeaPenStore();
    seaPenStore = getSeaPenStore();
  });

  test('sets recent sea pen images in store', async () => {
    await getRecentSeaPenImageIds(seaPenProvider, seaPenStore);

    assertDeepEquals(
        [
          beginLoadRecentSeaPenImagesAction(),
          setRecentSeaPenImagesAction(seaPenProvider.recentImageIds),
        ],
        personalizationStore.actions, 'recent images action');

    assertDeepEquals(
        seaPenProvider.recentImageIds,
        personalizationStore.data.wallpaper.seaPen.recentImages,
        'recent images set in store');
  });

  test('sets sea pen thumbnails in store', async () => {
    const query = seaPenProvider.seaPenQuery;
    await getSeaPenThumbnails(query, seaPenProvider, seaPenStore);
    assertDeepEquals(
        [
          beginSearchSeaPenThumbnailsAction(query),
          setCurrentSeaPenQueryAction(query),
          setThumbnailResponseStatusCodeAction(MantaStatusCode.kOk),
          setSeaPenThumbnailsAction(query, seaPenProvider.thumbnails),
        ],
        personalizationStore.actions, 'expected actions match');

    assertDeepEquals(
        [
          {
            'wallpaper.seaPen': typeCheck<SeaPenState>({
              loading: {
                recentImageData: {},
                recentImages: false,
                thumbnails: true,
                currentSelected: false,
                setImage: 0,
              },
              recentImageData: {},
              recentImages: null,
              thumbnailResponseStatusCode: null,
              thumbnails: null,
              currentSeaPenQuery: null,
              pendingSelected: null,
              currentSelected: null,
              shouldShowSeaPenIntroductionDialog: false,
              error: null,
              textQueryHistory: null,
            }),
          },
          {
            'wallpaper.seaPen': typeCheck<SeaPenState>({
              loading: {
                recentImageData: {},
                recentImages: false,
                thumbnails: true,
                currentSelected: false,
                setImage: 0,
              },
              recentImageData: {},
              recentImages: null,
              thumbnailResponseStatusCode: null,
              thumbnails: null,
              currentSeaPenQuery: query,
              pendingSelected: null,
              currentSelected: null,
              shouldShowSeaPenIntroductionDialog: false,
              error: null,
              textQueryHistory: null,
            }),
          },
          {
            'wallpaper.seaPen': typeCheck<SeaPenState>({
              loading: {
                recentImageData: {},
                recentImages: false,
                thumbnails: true,
                currentSelected: false,
                setImage: 0,
              },
              recentImageData: {},
              recentImages: null,
              thumbnailResponseStatusCode: MantaStatusCode.kOk,
              thumbnails: null,
              currentSeaPenQuery: query,
              pendingSelected: null,
              currentSelected: null,
              shouldShowSeaPenIntroductionDialog: false,
              error: null,
              textQueryHistory: null,
            }),
          },
          {
            'wallpaper.seaPen': typeCheck<SeaPenState>({
              loading: {
                recentImageData: {},
                recentImages: false,
                thumbnails: false,
                currentSelected: false,
                setImage: 0,
              },
              recentImageData: {},
              recentImages: null,
              thumbnailResponseStatusCode: MantaStatusCode.kOk,
              thumbnails: seaPenProvider.thumbnails,
              currentSeaPenQuery: query,
              pendingSelected: null,
              currentSelected: null,
              shouldShowSeaPenIntroductionDialog: false,
              error: null,
              textQueryHistory: null,
            }),
          },
        ],
        personalizationStore.states.map(
            filterAndFlattenState(['wallpaper.seaPen'])),
        'expected states match');
  });

  test('resets loading state after select thumbnail failure', async () => {
    seaPenProvider.selectSeaPenThumbnailResponse =
        Promise.resolve({success: false});
    personalizationStore.data.wallpaper.currentSelected = {
      type: WallpaperType.kSeaPen,
      key: '123',
      layout: WallpaperLayout.kCenterCropped,
      descriptionContent: '',
      descriptionTitle: '',
    };
    personalizationStore.data.wallpaper.seaPen.currentSelected = 123;

    const promise = selectSeaPenThumbnail(
        {image: {url: ''}, id: 456}, seaPenProvider, seaPenStore);

    assertDeepEquals(
        {image: true, attribution: true},
        personalizationStore.data.wallpaper.loading.selected,
        'image and attribution are loading');
    assertTrue(
        personalizationStore.data.wallpaper.seaPen.loading.currentSelected,
        'seaPen.loading.currentSelected is true');

    await promise;

    assertDeepEquals(
        {image: false, attribution: false},
        personalizationStore.data.wallpaper.loading.selected,
        'image and attribution are not loading');
    assertFalse(
        personalizationStore.data.wallpaper.seaPen.loading.currentSelected,
        'seaPen.loading.currentSelected is false');

    assertDeepEquals(
        [
          beginLoadSelectedImageAction(),
          beginSelectSeaPenThumbnailAction({image: {url: ''}, id: 456}),
          endSelectSeaPenThumbnailAction({image: {url: ''}, id: 456}, false),
          setFullscreenStateAction(FullscreenPreviewState.OFF),
          setSeaPenFullscreenStateAction(FullscreenPreviewState.OFF),
          setSelectedRecentSeaPenImageAction(123),
        ],
        personalizationStore.actions,
        'resets to original selected id after failure');
  });

  test('resets loading state after select recent image failure', async () => {
    seaPenProvider.selectSeaPenRecentImageResponse =
        Promise.resolve({success: false});
    personalizationStore.data.wallpaper.currentSelected = {
      type: WallpaperType.kSeaPen,
      key: '123',
      layout: WallpaperLayout.kCenterCropped,
      descriptionContent: '',
      descriptionTitle: '',
    };
    personalizationStore.data.wallpaper.seaPen.currentSelected = 123;

    const promise = selectRecentSeaPenImage(456, seaPenProvider, seaPenStore);

    assertDeepEquals(
        {image: true, attribution: true},
        personalizationStore.data.wallpaper.loading.selected,
        'image and attribution are loading');
    assertTrue(
        personalizationStore.data.wallpaper.seaPen.loading.currentSelected,
        'seaPen.loading.currentSelected is true');

    await promise;

    assertDeepEquals(
        {image: false, attribution: false},
        personalizationStore.data.wallpaper.loading.selected,
        'image and attribution are not loading');
    assertFalse(
        personalizationStore.data.wallpaper.seaPen.loading.currentSelected,
        'seaPen.loading.currentSelected is false');

    assertDeepEquals(
        [
          beginSelectRecentSeaPenImageAction(456),
          beginLoadSelectedImageAction(),
          beginLoadSelectedRecentSeaPenImageAction(),
          endSelectRecentSeaPenImageAction(456, false),
          setFullscreenStateAction(FullscreenPreviewState.OFF),
          setSeaPenFullscreenStateAction(FullscreenPreviewState.OFF),
          setSelectedRecentSeaPenImageAction(123),
        ],
        personalizationStore.actions,
        'resets to original selected id after failure');
  });

  test('sets error after select thumbnail failure', async () => {
    seaPenProvider.selectSeaPenThumbnailResponse =
        Promise.resolve({success: false});

    const thumbnail = {image: {url: ''}, id: 456};
    await selectSeaPenThumbnail(thumbnail, seaPenProvider, seaPenStore);

    assertDeepEquals(
        [
          beginLoadSelectedImageAction(),
          beginSelectSeaPenThumbnailAction(thumbnail),
          endSelectSeaPenThumbnailAction(thumbnail, false),
          setFullscreenStateAction(FullscreenPreviewState.OFF),
          setSeaPenFullscreenStateAction(FullscreenPreviewState.OFF),
          setSelectedRecentSeaPenImageAction(null),
        ],
        personalizationStore.actions, 'fails selecting the thumbnail');

    assertDeepEquals(
        [
          null,
          null,
          loadTimeData.getString('seaPenErrorGeneric'),
          loadTimeData.getString('seaPenErrorGeneric'),
          loadTimeData.getString('seaPenErrorGeneric'),
          loadTimeData.getString('seaPenErrorGeneric'),
        ],
        personalizationStore.states.map(state => state.wallpaper.seaPen.error),
        'sets expected error state');

    // Try and fail again.
    const promise =
        selectSeaPenThumbnail(thumbnail, seaPenProvider, seaPenStore);

    // Error reset to null while attempting to select again.
    assertEquals(null, personalizationStore.data.wallpaper.seaPen.error);

    await promise;

    // Error is visible again.
    assertEquals(
        loadTimeData.getString('seaPenErrorGeneric'),
        personalizationStore.data.wallpaper.seaPen.error);
  });

  test('sets error after select recent image failure', async () => {
    seaPenProvider.selectSeaPenRecentImageResponse =
        Promise.resolve({success: false});

    await selectRecentSeaPenImage(456, seaPenProvider, seaPenStore);

    assertDeepEquals(
        [
          beginSelectRecentSeaPenImageAction(456),
          beginLoadSelectedImageAction(),
          beginLoadSelectedRecentSeaPenImageAction(),
          endSelectRecentSeaPenImageAction(456, false),
          setFullscreenStateAction(FullscreenPreviewState.OFF),
          setSeaPenFullscreenStateAction(FullscreenPreviewState.OFF),
          setSelectedRecentSeaPenImageAction(null),
        ],
        personalizationStore.actions,
        'expected actions when failing to select recent image');

    assertDeepEquals(
        [
          null,
          null,
          null,
          loadTimeData.getString('seaPenErrorGeneric'),
          loadTimeData.getString('seaPenErrorGeneric'),
          loadTimeData.getString('seaPenErrorGeneric'),
          loadTimeData.getString('seaPenErrorGeneric'),
        ],
        personalizationStore.states.map(state => state.wallpaper.seaPen.error),
        'expected error states set when failing to select recent image');

    // Try and fail again.
    const promise = selectRecentSeaPenImage(789, seaPenProvider, seaPenStore);

    // Error reset to null while attempting to select again.
    assertEquals(null, personalizationStore.data.wallpaper.seaPen.error);

    await promise;

    // Error is visible again.
    assertEquals(
        loadTimeData.getString('seaPenErrorGeneric'),
        personalizationStore.data.wallpaper.seaPen.error);
  });

  test('select recent SeaPen image on clamshell mode', async () => {
    const {tabletMode} = await seaPenProvider.isInTabletMode();

    assertFalse(tabletMode);

    seaPenProvider.selectSeaPenRecentImageResponse =
        Promise.resolve({success: true});
    await selectRecentSeaPenImage(123, seaPenProvider, seaPenStore);

    assertDeepEquals(
        [
          beginSelectRecentSeaPenImageAction(123),
          beginLoadSelectedImageAction(),
          beginLoadSelectedRecentSeaPenImageAction(),
          endSelectRecentSeaPenImageAction(123, true),
        ],
        personalizationStore.actions,
        'expected actions when select recent image on clamshell mode');
  });

  test('select recent SeaPen image on tablet mode', async () => {
    seaPenProvider.isInTabletModeResponse = true;
    const {tabletMode} = await seaPenProvider.isInTabletMode();

    assertTrue(tabletMode);

    seaPenProvider.selectSeaPenRecentImageResponse =
        Promise.resolve({success: true});
    await selectRecentSeaPenImage(123, seaPenProvider, seaPenStore);

    assertDeepEquals(
        [
          beginSelectRecentSeaPenImageAction(123),
          beginLoadSelectedImageAction(),
          beginLoadSelectedRecentSeaPenImageAction(),
          setFullscreenStateAction(FullscreenPreviewState.LOADING),
          setSeaPenFullscreenStateAction(FullscreenPreviewState.LOADING),
          endSelectRecentSeaPenImageAction(123, true),
        ],
        personalizationStore.actions,
        'expected actions when select recent image on tablet mode');
  });
});