chromium/chrome/test/data/webui/chromeos/vc_background_ui/vc_background_ui_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 'chrome://resources/ash/common/cr_elements/cr_action_menu/cr_action_menu.js';

import {CrInputElement} from 'chrome://resources/ash/common/cr_elements/cr_input/cr_input.js';
import {WallpaperGridItemElement} from 'chrome://resources/ash/common/personalization/wallpaper_grid_item_element.js';
import {getSeaPenTemplates} from 'chrome://resources/ash/common/sea_pen/constants.js';
import {SeaPenPaths, SeaPenRouterElement} from 'chrome://resources/ash/common/sea_pen/sea_pen_router_element.js';
import {SeaPenTemplateQueryElement} from 'chrome://resources/ash/common/sea_pen/sea_pen_template_query_element.js';
import {setTransitionsEnabled} from 'chrome://resources/ash/common/sea_pen/transition.js';
import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
import {VcBackgroundApp} from 'chrome://vc-background/js/vc_background_app.js';
import {VcBackgroundBreadcrumbElement} from 'chrome://vc-background/js/vc_background_breadcrumb_element.js';
import {assertArrayEquals, assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
import {waitAfterNextRender} from 'chrome://webui-test/polymer_test_util.js';

suite('VcBackgroundUITest', () => {
  setup(() => {
    // Disables page transition by default.
    setTransitionsEnabled(false);
  });

  function getVcBackgroundApp(): VcBackgroundApp {
    const vcBackgroundApp = document.body.querySelector('vc-background-app');
    assertTrue(!!vcBackgroundApp, 'vc-background-app exists');
    return vcBackgroundApp!;
  }

  function getVcBackgroundBreadcrumbs(): VcBackgroundBreadcrumbElement {
    const vcBackgroundBreadcrumb =
        getVcBackgroundApp().shadowRoot!.querySelector(
            'vc-background-breadcrumb')!;
    assertTrue(!!vcBackgroundBreadcrumb, 'vc-background-breadcrumb exists');
    return vcBackgroundBreadcrumb!;
  }

  function getVcBackgroundBreadcrumbsText(): string[] {
    const breadcrumbElements =
        getVcBackgroundBreadcrumbs().shadowRoot!.querySelectorAll('cr-button')!;
    return Array.from(breadcrumbElements).map(el => el.textContent!);
  }

  function getSeaPenRouter(): SeaPenRouterElement {
    const seaPenRouter =
        getVcBackgroundApp().shadowRoot!.querySelector('sea-pen-router');
    assertTrue(!!seaPenRouter, 'sea-pen-router exists');
    return seaPenRouter;
  }

  function getSeaPenTemplateQuery(): SeaPenTemplateQueryElement|null {
    return getSeaPenRouter().shadowRoot!.querySelector(
        'sea-pen-template-query');
  }

  function getSeaPenTemplateElements(): WallpaperGridItemElement[] {
    const seaPenTemplates =
        getSeaPenRouter().shadowRoot?.querySelector('sea-pen-templates');
    assertTrue(!!seaPenTemplates, 'sea-pen-templates exists');
    return Array.from(seaPenTemplates.shadowRoot!.querySelectorAll(
        'wallpaper-grid-item:not([hidden])'));
  }

  test('initial breadcrumbs', async () => {
    assertArrayEquals(
        getVcBackgroundBreadcrumbsText(),
        [getVcBackgroundBreadcrumbs().i18n('vcBackgroundLabel')]);
  });

  test('shows template options when template is clicked', async () => {
    assertFalse(
        !!getSeaPenTemplateQuery(),
        'sea-pen-template-query does not exist yet');

    const seaPenTemplateElements = getSeaPenTemplateElements();
    assertEquals(
        getSeaPenTemplates().length, seaPenTemplateElements.length,
        'each sea pen template is displayed');

    // Click the 'Classic art' template.
    seaPenTemplateElements
        .find(template => {
          const p = template.shadowRoot?.querySelector('p.primary-text');
          return p?.textContent?.trim() === 'Classic art';
        })!.click();
    await waitAfterNextRender(getSeaPenRouter());

    const seaPenTemplateQuery = getSeaPenTemplateQuery();
    assertTrue(!!seaPenTemplateQuery, 'sea-pen-template-query exists');

    const templateTokens =
        seaPenTemplateQuery.shadowRoot!.querySelectorAll<HTMLElement>(
            '.chip-text, .template-text');
    assertTrue(
        templateTokens.length > 0, 'template tokens should be available');

    const templateText: string[] = [];
    templateTokens.forEach((currentToken: HTMLElement) => {
      if (currentToken.classList.contains('chip-text')) {
        templateText.push(
            currentToken.shadowRoot?.getElementById('chipText')?.innerText ??
            '');
      } else {
        templateText.push(currentToken.innerText ?? '');
      }
    });

    assertEquals(
        'A painting of a field of flowers in the avant-garde style',
        templateText.join(' '),
        'Expected template text is shown for Classic art');

    assertEquals(
        'chrome://vc-background/results?seaPenTemplateId=104',
        window.location.href,
        'VC Background Classic art template id is added to url');

    // Breadcrumbs should show 'Classic art'.
    assertArrayEquals(getVcBackgroundBreadcrumbsText(), [
      getVcBackgroundBreadcrumbs().i18n('vcBackgroundLabel'),
      'Classic art',
    ]);
  });

  test(
      'verifies breadcrumbs when create button clicked in a template page',
      async () => {
        const seaPenRouter = getSeaPenRouter();
        seaPenRouter.goToRoute(SeaPenPaths.TEMPLATES);
        await waitAfterNextRender(seaPenRouter);

        const seaPenTemplateElements = getSeaPenTemplateElements();
        await waitAfterNextRender(seaPenRouter);

        // Click the 'Classic art' template.
        seaPenTemplateElements
            .find(template => {
              const p = template.shadowRoot?.querySelector('p.primary-text');
              return p?.textContent?.trim() === 'Classic art';
            })!.click();
        await waitAfterNextRender(seaPenRouter);

        const seaPenTemplateQuery = getSeaPenTemplateQuery();
        assertTrue(!!seaPenTemplateQuery, 'sea-pen-template-query exists');

        // Click on the 'Create' button.
        const createButton = seaPenTemplateQuery.shadowRoot?.querySelector(
                                 'cr-button#searchButton')! as HTMLElement;
        createButton.click();

        await waitAfterNextRender(seaPenTemplateQuery);

        assertEquals(
            'chrome://vc-background/results?seaPenTemplateId=104',
            window.location.href,
            'App is on /results and Classic art template id is added to url');

        // Breadcrumbs should show 'Classic art'.
        assertArrayEquals(getVcBackgroundBreadcrumbsText(), [
          getVcBackgroundBreadcrumbs().i18n('vcBackgroundLabel'),
          'Classic art',
        ]);
      });

  test('verifies breadcrumbs when routing to Freeform subpages', async () => {
    loadTimeData.overrideValues({isSeaPenTextInputEnabled: true});
    const seaPenRouter = getSeaPenRouter();
    seaPenRouter.goToRoute(SeaPenPaths.FREEFORM);
    await waitAfterNextRender(seaPenRouter);

    assertEquals(
        'chrome://vc-background/freeform', window.location.href,
        'routed to Freeform page');
    assertTrue(!!getVcBackgroundBreadcrumbs(), 'breadcrumb should display');
    assertArrayEquals(getVcBackgroundBreadcrumbsText(), ['AI Prompting']);

    // Search for a freeform query.
    const seaPenInputQuery =
        seaPenRouter.shadowRoot?.querySelector<HTMLElement>(
            'sea-pen-input-query');
    assertTrue(!!seaPenInputQuery, 'input query element exists');
    const inputElement =
        seaPenInputQuery.shadowRoot?.querySelector<CrInputElement>(
            '#queryInput');
    assertTrue(!!inputElement, 'freeform input displays');
    inputElement!.value = 'a cool castle';
    seaPenInputQuery.shadowRoot?.getElementById('searchButton')!.click();

    assertEquals(
        'chrome://vc-background/freeform', window.location.href,
        'should stay in the same Freeform page');

    // Breadcrumbs remain the same for Freeform page.
    assertArrayEquals(getVcBackgroundBreadcrumbsText(), ['AI Prompting']);
  });

  test('allows changing templates via breadcrumbs dropdown menu', async () => {
    // Navigate directly to a results page with template in breadcrumbs.
    const seaPenRouter = getSeaPenRouter();
    seaPenRouter.goToRoute(SeaPenPaths.RESULTS, {seaPenTemplateId: '104'});
    await waitAfterNextRender(seaPenRouter);

    const breadcrumbElement = getVcBackgroundBreadcrumbs();

    const classicArtTitle = 'Classic art';
    // Breadcrumbs should show 'Classic art'.
    assertArrayEquals(
        getVcBackgroundBreadcrumbsText(),
        [breadcrumbElement.i18n('vcBackgroundLabel'), classicArtTitle]);

    const dropdownMenu =
        breadcrumbElement.shadowRoot!.querySelector('cr-action-menu');
    assertTrue(!!dropdownMenu);
    assertFalse(dropdownMenu!.open, 'the action menu should not open yet');

    // Verify the selected template.
    const selectedElement =
        dropdownMenu.querySelectorAll('button[aria-checked=\'true\']');
    assertEquals(
        1, selectedElement.length, 'there should be one template selected');
    assertEquals(
        classicArtTitle, (selectedElement[0] as HTMLElement)!.innerText.trim(),
        `selected template in the dropdown should be ${classicArtTitle}`);

    // Activate the dropdown menu.
    const lastBreadcrumb = breadcrumbElement.shadowRoot!.querySelector(
                               'cr-button#breadcrumb1') as HTMLElement;
    lastBreadcrumb.click();

    await waitAfterNextRender(breadcrumbElement);

    assertTrue(dropdownMenu!.open, 'the action menu should be open');

    // The dropdown should show the sea pen templates.
    const allMenuItems = dropdownMenu.querySelectorAll('button');
    assertEquals(allMenuItems.length, getSeaPenTemplates().length);

    // Click on the 'Office' template.
    const officeTitle = 'Stylish office';
    Array.from(allMenuItems)
        .find(el => el.textContent?.trim() === officeTitle)!.click();

    await waitAfterNextRender(breadcrumbElement);

    // We should be redirected to the new template page.
    assertEquals(
        'chrome://vc-background/results?seaPenTemplateId=101',
        window.location.href,
        'App is on /results and Office template id is added to url');

    // Breadcrumbs should now show 'Stylish office'.
    assertArrayEquals(
        getVcBackgroundBreadcrumbsText(),
        [getVcBackgroundBreadcrumbs().i18n('vcBackgroundLabel'), officeTitle]);
  });
});