chromium/chrome/test/data/webui/password_manager/share_password_confirmation_dialog_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://password-manager/password_manager.js';

import type {SharePasswordConfirmationDialogElement} from 'chrome://password-manager/password_manager.js';
import {PasswordManagerImpl} from 'chrome://password-manager/password_manager.js';
import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
import {MockTimer} from 'chrome://webui-test/mock_timer.js';
import {isVisible} from 'chrome://webui-test/test_util.js';

import {TestPasswordManagerProxy} from './test_password_manager_proxy.js';
import {createPasswordEntry, makeRecipientInfo} from './test_util.js';

const SHARED_PASSWORD_ID = 42;
const SHARED_PASSWORD_NAME = 'example.com';
const HTTPS_CHANGE_PASSWORD_URL = 'https://example.com';
const HTTP_CHANGE_PASSWORD_URL = 'http://example.com';

function assertVisibleTextContent(element: HTMLElement, expectedText: string) {
  assertTrue(isVisible(element));
  assertEquals(expectedText, element?.textContent!.trim());
}

suite('SharePasswordConfirmationDialogTest', function() {
  let dialog: SharePasswordConfirmationDialogElement;
  let passwordManager: TestPasswordManagerProxy;
  let mockTimer: MockTimer;

  setup(function() {
    document.body.innerHTML = window.trustedTypes!.emptyHTML;
    mockTimer = new MockTimer();
    mockTimer.install();
    passwordManager = new TestPasswordManagerProxy();
    PasswordManagerImpl.setInstance(passwordManager);
    dialog = document.createElement('share-password-confirmation-dialog');
    dialog.passwordName = SHARED_PASSWORD_NAME;
    dialog.password = createPasswordEntry({
      id: SHARED_PASSWORD_ID,
      changePasswordUrl: HTTPS_CHANGE_PASSWORD_URL,
    });
    dialog.recipients = [makeRecipientInfo()];
    document.body.appendChild(dialog);
    flush();
  });

  teardown(function() {
    mockTimer.uninstall();
  });

  test('Has correct loading state', function() {
    assertVisibleTextContent(
        dialog.$.header, dialog.i18n('shareDialogLoadingTitle'));
    assertVisibleTextContent(dialog.$.cancel, dialog.i18n('cancel'));
    assertFalse(isVisible(dialog.$.done));
    assertFalse(isVisible(dialog.$.description));
    assertFalse(isVisible(dialog.$.footerDescription));
  });

  test('Has correct canceled state', function() {
    assertTrue(isVisible(dialog.$.cancel));
    dialog.$.cancel.click();
    flush();

    assertVisibleTextContent(
        dialog.$.header, dialog.i18n('shareDialogCanceledTitle'));
    assertVisibleTextContent(dialog.$.done, dialog.i18n('done'));
    assertFalse(isVisible(dialog.$.cancel));
    assertFalse(isVisible(dialog.$.description));
    assertFalse(isVisible(dialog.$.footerDescription));
  });

  test('Has correct success state', async function() {
    // The user has 5 seconds to cancel the dialog, after that `sharePassword`
    // is triggered.
    mockTimer.tick(5000);
    flush();

    assertVisibleTextContent(
        dialog.$.header, dialog.i18n('shareDialogSuccessTitle'));

    const [actualId, actualRecipients] =
        await passwordManager.whenCalled('sharePassword');
    assertEquals(SHARED_PASSWORD_ID, actualId);
    assertEquals(1, actualRecipients.length);
  });

  test(
      'Has correct success state with single recipient (https site)',
      async function() {
        // The user has 5 seconds to cancel the dialog, after that
        // `sharePassword` is triggered.
        mockTimer.tick(5000);
        flush();

        assertVisibleTextContent(
            dialog.$.header, dialog.i18n('shareDialogSuccessTitle'));

        assertTrue(isVisible(dialog.$.description));
        assertEquals(
            dialog.$.description.innerHTML,
            dialog
                .i18nAdvanced(
                    'sharePasswordConfirmationDescriptionSingleRecipient', {
                      substitutions: [
                        'New User',
                        SHARED_PASSWORD_NAME,
                      ],
                    })
                .toString());
        assertTrue(isVisible(dialog.$.footerDescription));
        assertEquals(
            dialog.$.footerDescription.innerHTML,
            dialog
                .i18nAdvanced('sharePasswordConfirmationFooterWebsite', {
                  substitutions: [
                    `<a href='${HTTPS_CHANGE_PASSWORD_URL}' target='_blank'>${
                        SHARED_PASSWORD_NAME}</a>`,
                  ],
                })
                .toString());

        const [actualId, actualRecipients] =
            await passwordManager.whenCalled('sharePassword');
        assertEquals(SHARED_PASSWORD_ID, actualId);
        assertEquals(1, actualRecipients.length);
      });

  test(
      'Has correct success footer with single recipient (http site)',
      async function() {
        dialog.password = createPasswordEntry({
          id: SHARED_PASSWORD_ID,
          changePasswordUrl: HTTP_CHANGE_PASSWORD_URL,
        });

        // The user has 5 seconds to cancel the dialog, after that
        // `sharePassword` is triggered.
        mockTimer.tick(5000);
        flush();

        assertEquals(
            dialog.$.footerDescription.innerHTML,
            dialog
                .i18nAdvanced('sharePasswordConfirmationFooterWebsite', {
                  substitutions: [
                    SHARED_PASSWORD_NAME,
                  ],
                })
                .toString());

        const [actualId, actualRecipients] =
            await passwordManager.whenCalled('sharePassword');
        assertEquals(SHARED_PASSWORD_ID, actualId);
        assertEquals(1, actualRecipients.length);
      });

  test('Has correct footer for shared Android creadential', async function() {
    // Credential without change password url is an Android credentail.
    dialog.password = createPasswordEntry({
      id: SHARED_PASSWORD_ID,
    });
    // The user has 5 seconds to cancel the dialog, after that `sharePassword`
    // is triggered.
    mockTimer.tick(5000);
    flush();

    assertTrue(isVisible(dialog.$.footerDescription));
    assertEquals(
        dialog.$.footerDescription.innerHTML,
        dialog.i18nAdvanced('sharePasswordConfirmationFooterAndroidApp')
            .toString());

    await passwordManager.whenCalled('sharePassword');
  });

  test(
      'Has correct success description for multiple recipients',
      async function() {
        dialog.recipients = [makeRecipientInfo(), makeRecipientInfo()];
        // The user has 5 seconds to cancel the dialog, after that
        // `sharePassword` is triggered.
        mockTimer.tick(5000);
        flush();

        assertVisibleTextContent(
            dialog.$.header, dialog.i18n('shareDialogSuccessTitle'));

        assertTrue(isVisible(dialog.$.description));
        assertEquals(
            dialog.$.description.innerHTML,
            dialog
                .i18nAdvanced(
                    'sharePasswordConfirmationDescriptionMultipleRecipients', {
                      substitutions: [
                        SHARED_PASSWORD_NAME,
                      ],
                    })
                .toString());

        const [actualId, actualRecipients] =
            await passwordManager.whenCalled('sharePassword');
        assertEquals(SHARED_PASSWORD_ID, actualId);
        assertEquals(2, actualRecipients.length);
      });
});