Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/actions/pnpm-setup/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ inputs:
node-version:
description: Node.js version
required: false
default: "22"
default: "24"

runs:
using: composite
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { createTestBed } from "../support/testbed.ts";

test.describe("Device activity", () => {
test("signs out of a single device session", async ({ browser }) => {
const realm = await createTestBed();
await using testBed = await createTestBed();
const context1 = await browser.newContext();
const context2 = await browser.newContext();

Expand All @@ -13,13 +13,13 @@ test.describe("Device activity", () => {
const page2 = await context2.newPage();

// Log in the first session, and verify it is active.
await login(page1, realm);
await login(page1, testBed.realm);
await page1.getByTestId("accountSecurity").click();
await page1.getByTestId("account-security/device-activity").click();
await expect(page1.getByTestId("row-0")).toContainText("Current session");

// Log in the second session, and verify it is active.
await login(page2, realm);
await login(page2, testBed.realm);
await page2.getByTestId("accountSecurity").click();
await page2.getByTestId("account-security/device-activity").click();
await expect(page2.getByTestId("row-0")).toContainText("Current session");
Expand Down Expand Up @@ -48,7 +48,7 @@ test.describe("Device activity", () => {
});

test("signs out of all device sessions", async ({ browser }) => {
const realm = await createTestBed();
await using testBed = await createTestBed();
const context1 = await browser.newContext();
const context2 = await browser.newContext();

Expand All @@ -57,8 +57,8 @@ test.describe("Device activity", () => {
const page2 = await context2.newPage();

// Log in both sessions, then sign out of all devices from the second session.
await login(page1, realm);
await login(page2, realm);
await login(page1, testBed.realm);
await login(page2, testBed.realm);
await page2.getByTestId("accountSecurity").click();
await page2.getByTestId("account-security/device-activity").click();
await page2
Expand Down
26 changes: 13 additions & 13 deletions js/apps/account-ui/test/account-security/linked-accounts.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,10 @@ const EXTERNAL_EMAIL = "external-user@keycloak.org";

test.describe("Linked accounts", () => {
test("shows linked accounts", async ({ page }) => {
const realm = await createTestBed(userProfileRealm);
await using testBed = await createTestBed(userProfileRealm);

// Log in and navigate to the linked accounts section.
await login(page, realm);
await login(page, testBed.realm);
await page.getByTestId("accountSecurity").click();
await page.getByTestId("account-security/linked-accounts").click();
await expect(page.getByTestId("page-heading")).toHaveText(
Expand All @@ -25,7 +25,7 @@ test.describe("Linked accounts", () => {

test("cannot remove the last federated identity", async ({ page }) => {
// Create an 'external' realm with a user that will be used for linking.
const externalRealm = await createTestBed({
await using externalTestBed = await createTestBed({
users: [
{
...DEFAULT_USER,
Expand All @@ -42,32 +42,32 @@ test.describe("Linked accounts", () => {
});

await adminClient.clients.create({
realm: externalRealm,
realm: externalTestBed.realm,
...groupsIdPClient,
});

// Create a realm that links to the external realm as an identity provider.
const realm = await createTestBed();
await using testBed = await createTestBed();

await adminClient.identityProviders.create({
realm,
realm: testBed.realm,
alias: "master-idp",
providerId: "oidc",
enabled: true,
config: {
clientId: "groups-idp",
clientSecret: "H0JaTc7VBu3HJR26vrzMxgidfJmgI5Dw",
validateSignature: "false",
tokenUrl: `${SERVER_URL}realms/${externalRealm}/protocol/openid-connect/token`,
jwksUrl: `${SERVER_URL}realms/${externalRealm}/protocol/openid-connect/certs`,
issuer: `${SERVER_URL}realms/${externalRealm}`,
authorizationUrl: `${SERVER_URL}realms/${externalRealm}/protocol/openid-connect/auth`,
logoutUrl: `${SERVER_URL}realms/${externalRealm}/protocol/openid-connect/logout`,
userInfoUrl: `${SERVER_URL}realms/${externalRealm}/protocol/openid-connect/userinfo`,
tokenUrl: `${SERVER_URL}realms/${externalTestBed.realm}/protocol/openid-connect/token`,
jwksUrl: `${SERVER_URL}realms/${externalTestBed.realm}/protocol/openid-connect/certs`,
issuer: `${SERVER_URL}realms/${externalTestBed.realm}`,
authorizationUrl: `${SERVER_URL}realms/${externalTestBed.realm}/protocol/openid-connect/auth`,
logoutUrl: `${SERVER_URL}realms/${externalTestBed.realm}/protocol/openid-connect/logout`,
userInfoUrl: `${SERVER_URL}realms/${externalTestBed.realm}/protocol/openid-connect/userinfo`,
},
});

await page.goto(getAccountUrl(realm).toString());
await page.goto(getAccountUrl(testBed.realm).toString());

// Click the login via master-idp provider button
await loginWithIdp(page, "master-idp");
Expand Down
14 changes: 7 additions & 7 deletions js/apps/account-ui/test/account-security/signing-in.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@ import { createTestBed } from "../support/testbed.ts";

test.describe("Signing in", () => {
test("shows password and OTP credentials", async ({ page }) => {
const realm = await createTestBed();
await using testBed = await createTestBed();

// Log in and navigate to the signing in section.
await login(page, realm);
await login(page, testBed.realm);
await page.getByTestId("accountSecurity").click();
await page.getByTestId("account-security/signing-in").click();

Expand Down Expand Up @@ -38,17 +38,17 @@ test.describe("Signing in", () => {
test("allows setting a password credential if none exists", async ({
page,
}) => {
const realm = await createTestBed();
const user = await findUserByUsername(realm, DEFAULT_USER.username);
await using testBed = await createTestBed();
const user = await findUserByUsername(testBed.realm, DEFAULT_USER.username);

// Log in and delete the password credential of the user.
await login(page, realm);
await login(page, testBed.realm);
const credentials = await adminClient.users.getCredentials({
realm,
realm: testBed.realm,
id: user.id as string,
});
await adminClient.users.deleteCredential({
realm,
realm: testBed.realm,
id: user.id as string,
credentialId: credentials[0].id as string,
});
Expand Down
4 changes: 2 additions & 2 deletions js/apps/account-ui/test/applications.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@ test.describe("Applications", () => {
test("shows a list of applications the user has access to", async ({
page,
}) => {
const realm = await createTestBed();
await using testBed = await createTestBed();

// Log in and navigate to the applications page.
await login(page, realm);
await login(page, testBed.realm);
await page.getByTestId("applications").click();

// Assert that the applications list is displayed and contains the expected application.
Expand Down
8 changes: 4 additions & 4 deletions js/apps/account-ui/test/groups.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,17 @@ import { createTestBed } from "./support/testbed.ts";

test.describe("Groups", () => {
test("lists groups", async ({ page }) => {
const realm = await createTestBed(groupsRealm);
await using testBed = await createTestBed(groupsRealm);

await login(page, realm);
await login(page, testBed.realm);
await page.getByTestId("groups").click();
await expect(page.getByTestId("group[1].name")).toHaveText("three");
});

test("lists direct and indirect groups", async ({ page }) => {
const realm = await createTestBed(groupsRealm);
await using testBed = await createTestBed(groupsRealm);

await login(page, realm, "alice", "alice");
await login(page, testBed.realm, "alice", "alice");
await page.getByTestId("groups").click();

await expect(
Expand Down
44 changes: 28 additions & 16 deletions js/apps/account-ui/test/personal-info/personal-info.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@
import userProfileRealm from "../realms/user-profile-realm.json" with { type: "json" };

test.describe("Personal info", () => {
test("sets basic information", async ({ page }) => {

Check warning on line 9 in js/apps/account-ui/test/personal-info/personal-info.spec.ts

View workflow job for this annotation

GitHub Actions / Account UI

Test has no assertions
const realm = await createTestBed();
await using testBed = await createTestBed();

await login(page, realm);
await login(page, testBed.realm);

await page.getByTestId("email").fill("edewit@somewhere.com");
await page.getByTestId("firstName").fill("Erik");
Expand All @@ -22,10 +22,13 @@

test.describe("Personal info (user profile enabled)", () => {
test("renders user profile fields", async ({ page }) => {
const realm = await createTestBed(userProfileRealm);
await using testBed = await createTestBed(userProfileRealm);

await adminClient.users.updateProfile({ ...userProfile, realm });
await login(page, realm);
await adminClient.users.updateProfile({
...userProfile,
realm: testBed.realm,
});
await login(page, testBed.realm);

await expect(page.locator("#select")).toBeVisible();
await expect(page.getByTestId("help-label-select")).toBeVisible();
Expand All @@ -36,13 +39,16 @@
});

test("renders long select options as typeahead", async ({ page }) => {
const realm = await createTestBed(userProfileRealm);
await using testBed = await createTestBed(userProfileRealm);

await adminClient.users.updateProfile({ ...userProfile, realm });
await login(page, realm);
await adminClient.users.updateProfile({
...userProfile,
realm: testBed.realm,
});
await login(page, testBed.realm);

await page.locator("#alternatelang").click();
await page.waitForSelector("text=Italiano");

Check warning on line 51 in js/apps/account-ui/test/personal-info/personal-info.spec.ts

View workflow job for this annotation

GitHub Actions / Account UI

Unexpected use of page.waitForSelector()

await page.locator("#alternatelang").click();
await page.locator("*:focus").press("Control+A");
Expand All @@ -53,13 +59,16 @@
});

test("renders long list of locales as typeahead", async ({ page }) => {
const realm = await createTestBed(userProfileRealm);
await using testBed = await createTestBed(userProfileRealm);

await adminClient.users.updateProfile({ ...userProfile, realm });
await login(page, realm);
await adminClient.users.updateProfile({
...userProfile,
realm: testBed.realm,
});
await login(page, testBed.realm);

await page.locator("#attributes\\.locale").click();
await page.waitForSelector("text=Italiano");

Check warning on line 71 in js/apps/account-ui/test/personal-info/personal-info.spec.ts

View workflow job for this annotation

GitHub Actions / Account UI

Unexpected use of page.waitForSelector()

await page.locator("#attributes\\.locale").click();
await page.locator("*:focus").press("Control+A");
Expand All @@ -70,10 +79,13 @@
});

test("saves user profile", async ({ page }) => {
const realm = await createTestBed(userProfileRealm);
await using testBed = await createTestBed(userProfileRealm);

await adminClient.users.updateProfile({ ...userProfile, realm });
await login(page, realm);
await adminClient.users.updateProfile({
...userProfile,
realm: testBed.realm,
});
await login(page, testBed.realm);

await page.locator("#select").click();
await page.getByRole("option", { name: "two" }).click();
Expand Down Expand Up @@ -101,12 +113,12 @@

test.describe("Realm localization", () => {
test("changes locale", async ({ page }) => {
const realm = await createTestBed({
await using testBed = await createTestBed({
internationalizationEnabled: true,
supportedLocales: ["en", "nl", "de"],
});

await login(page, realm);
await login(page, testBed.realm);
await page.locator("#attributes\\.locale").click();
page.getByRole("option").filter({ hasText: "Deutsch" });
await page.getByRole("option", { name: "English" }).click();
Expand Down
21 changes: 14 additions & 7 deletions js/apps/account-ui/test/resources.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,33 @@ import type RealmRepresentation from "@keycloak/keycloak-admin-client/lib/defs/r
import { expect, test } from "@playwright/test";
import resourcesRealm from "./realms/resources-realm.json" with { type: "json" };
import { login } from "./support/actions.ts";
import { createTestBed } from "./support/testbed.ts";
import { createTestBed, type TestBed } from "./support/testbed.ts";

test.describe("Resources", () => {
// The test cases in this suite depend on state created in previous tests.
// Therefore, we run them in serial mode.
// TODO: Refactor tests to be independent and run in parallel.
test.describe.configure({ mode: "serial" });

let realm: string;
let testBed: TestBed;

test.beforeAll(async () => {
realm = await createTestBed(resourcesRealm as RealmRepresentation);
testBed = await createTestBed(resourcesRealm as RealmRepresentation);
});

test.afterAll(async () => {
await testBed[Symbol.asyncDispose]();
});

test("shows the resources owned by the user", async ({ page }) => {
await login(page, realm);
await login(page, testBed.realm);
await page.getByTestId("resources").click();

await expect(page.getByRole("gridcell", { name: "one" })).toBeVisible();
});

test("shows no resources are shared with another user", async ({ page }) => {
await login(page, realm, "alice", "alice");
await login(page, testBed.realm, "alice", "alice");
await page.getByTestId("resources").click();

await page.getByTestId("sharedWithMe").click();
Expand All @@ -30,7 +37,7 @@ test.describe("Resources", () => {
});

test("shares a recourse with another user", async ({ page }) => {
await login(page, realm);
await login(page, testBed.realm);
await page.getByTestId("resources").click();

await page.getByTestId("expand-one").click();
Expand Down Expand Up @@ -62,7 +69,7 @@ test.describe("Resources", () => {
});

test("shows the resources shared with another user", async ({ page }) => {
await login(page, realm, "alice", "alice");
await login(page, testBed.realm, "alice", "alice");
await page.getByTestId("resources").click();

await page.getByTestId("sharedWithMe").click();
Expand Down
15 changes: 12 additions & 3 deletions js/apps/account-ui/test/support/testbed.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,24 @@ import type RealmRepresentation from "@keycloak/keycloak-admin-client/lib/defs/r
import { adminClient } from "./admin-client.ts";
import { DEFAULT_USER } from "./common.ts";

export interface TestBed extends AsyncDisposable {
realm: string;
}

export async function createTestBed(
overrides?: RealmRepresentation,
): Promise<string> {
const { realmName } = await adminClient.realms.create({
): Promise<TestBed> {
const { realmName: realm } = await adminClient.realms.create({
enabled: true,
users: [DEFAULT_USER],
...overrides,
realm: crypto.randomUUID(),
});

return realmName;
const deleteRealm = () => adminClient.realms.del({ realm });

return {
realm,
[Symbol.asyncDispose]: deleteRealm,
};
}
2 changes: 1 addition & 1 deletion js/apps/admin-ui/CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ This project is the next generation of the Keycloak Administration UI. It is wri

### Prerequisites

Make sure that you have Node.js version 18 (or later) installed on your system. If you do not have Node.js installed we recommend using [Node Version Manager](https://github.com/nvm-sh/nvm) to install it.
Make sure that you have Node.js version 24 (or later) [installed on your system](https://nodejs.org/en/download).

You can find out which version of Node.js you are using by running the following command:

Expand Down
Loading
Loading