Ctrl K
ComunidadGuías…

tests/passkeys.spec.ts

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
import { expect } from "@playwright/test";

import { createPasskeys } from "../apps/backend/src/database/seeds";
import { loggedTest } from "./logged-test";
import { getUniqueTestIdentifier, screenshot } from "./util";

loggedTest(
  "passkeys - list, rename and delete",
  async ({ page, auth }, testInfo) => {
    await createPasskeys({
      userId: auth.user.id,
      names: ["1Password", "iCloud Keychain"],
      keyPrefix: `${getUniqueTestIdentifier(testInfo)}-`,
    });

    await page.goto(`/${auth.account.slug}/settings/authentication`);

    const card = page.getByRole("group", { name: "Passkeys" });
    const toggle = card.getByRole("button", { name: "2 passkeys registered" });
    await expect(toggle).toBeVisible();

    // The credentials stay hidden until the row is expanded.
    await expect(card.getByText("1Password")).toBeHidden();
    await toggle.click();
    await expect(card.getByText("1Password")).toBeVisible();
    await screenshot(page, "settings/passkeys", { element: card });

    await card.getByRole("button", { name: "Rename 1Password" }).click();
    const editDialog = page.getByRole("dialog");
    await editDialog
      .getByRole("textbox", { name: "Device Name" })
      .fill("Work laptop");
    await screenshot(page, "settings/passkeys-edit", { element: editDialog });
    await editDialog.getByRole("button", { name: "Save" }).click();
    await expect(card.getByText("Work laptop")).toBeVisible();
    // Opening the next dialog while this one is still leaving scrolls the row
    // into view over a page that has not settled, and it lands at one of two
    // offsets. Nothing of that shows except through the delete dialog's rounded
    // corners — which is exactly what kept its screenshot flaky.
    await expect(editDialog).toBeHidden();

    await card.getByRole("button", { name: "Delete Work laptop" }).click();
    const deleteDialog = page.getByRole("alertdialog");
    await screenshot(page, "settings/passkeys-delete", {
      element: deleteDialog,
    });
    await deleteDialog.getByRole("button", { name: "Delete" }).click();

    await expect(
      card.getByRole("button", { name: "1 passkey registered" }),
    ).toBeVisible();
    await expect(card.getByText("Work laptop")).toBeHidden();
  },
);

loggedTest("passkeys - create dialog", async ({ page, auth }) => {
  await page.goto(`/${auth.account.slug}/settings/authentication`);

  const card = page.getByRole("group", { name: "Passkeys" });
  await expect(card.getByText("No passkeys registered")).toBeVisible();
  await card.getByRole("button", { name: "Add" }).click();

  const dialog = page.getByRole("dialog");
  await expect(
    dialog.getByRole("heading", { name: "Create Passkey" }),
  ).toBeVisible();
  await screenshot(page, "settings/passkeys-create", { element: dialog });
});

loggedTest(
  "passkeys - register then sign in with it",
  async ({ page, auth, browserName }) => {
    loggedTest.skip(
      browserName !== "chromium",
      "Virtual authenticators are a Chrome DevTools Protocol feature",
    );

    // A software authenticator inside the browser, so the ceremonies below are
    // the real thing: actual `navigator.credentials` calls, actual signatures.
    const cdp = await page.context().newCDPSession(page);
    await cdp.send("WebAuthn.enable");
    await cdp.send("WebAuthn.addVirtualAuthenticator", {
      options: {
        protocol: "ctap2",
        transport: "internal",
        hasResidentKey: true,
        hasUserVerification: true,
        isUserVerified: true,
        automaticPresenceSimulation: true,
      },
    });

    await page.goto(`/${auth.account.slug}/settings/authentication`);
    const card = page.getByRole("group", { name: "Passkeys" });
    await card.getByRole("button", { name: "Add" }).click();
    await page
      .getByRole("dialog")
      .getByRole("button", { name: "Continue" })
      .click();
    await expect(
      card.getByRole("button", { name: "1 passkey registered" }),
    ).toBeVisible();

    // Sign out, then back in with nothing but the passkey — no email typed.
    await page.context().clearCookies();
    await page.goto("/login");
    await page.getByRole("button", { name: "Continue with Passkey" }).click();
    await expect(
      page.getByRole("button", { name: "Continue with Passkey" }),
    ).toBeHidden();

    // Reaching the settings page proves the session; "Last used" proves the
    // login went through this credential.
    await page.goto(`/${auth.account.slug}/settings/authentication`);
    await card.getByRole("button", { name: "1 passkey registered" }).click();
    await expect(card.getByText("Last used")).toBeVisible();
  },
);