Automatically dispose of realms created by createTestBed() (#43299)

Closes #43298

Signed-off-by: Jon Koops <jonkoops@gmail.com>
This commit is contained in:
Jon Koops 2025-10-10 16:22:21 +02:00 committed by GitHub
parent 934ac48a54
commit 5cbba8f984
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
14 changed files with 152 additions and 101 deletions

View File

@ -5,7 +5,7 @@ inputs:
node-version: node-version:
description: Node.js version description: Node.js version
required: false required: false
default: "22" default: "24"
runs: runs:
using: composite using: composite

View File

@ -4,7 +4,7 @@ import { createTestBed } from "../support/testbed.ts";
test.describe("Device activity", () => { test.describe("Device activity", () => {
test("signs out of a single device session", async ({ browser }) => { 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 context1 = await browser.newContext();
const context2 = await browser.newContext(); const context2 = await browser.newContext();
@ -13,13 +13,13 @@ test.describe("Device activity", () => {
const page2 = await context2.newPage(); const page2 = await context2.newPage();
// Log in the first session, and verify it is active. // 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("accountSecurity").click();
await page1.getByTestId("account-security/device-activity").click(); await page1.getByTestId("account-security/device-activity").click();
await expect(page1.getByTestId("row-0")).toContainText("Current session"); await expect(page1.getByTestId("row-0")).toContainText("Current session");
// Log in the second session, and verify it is active. // 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("accountSecurity").click();
await page2.getByTestId("account-security/device-activity").click(); await page2.getByTestId("account-security/device-activity").click();
await expect(page2.getByTestId("row-0")).toContainText("Current session"); await expect(page2.getByTestId("row-0")).toContainText("Current session");
@ -48,7 +48,7 @@ test.describe("Device activity", () => {
}); });
test("signs out of all device sessions", async ({ browser }) => { test("signs out of all device sessions", async ({ browser }) => {
const realm = await createTestBed(); await using testBed = await createTestBed();
const context1 = await browser.newContext(); const context1 = await browser.newContext();
const context2 = await browser.newContext(); const context2 = await browser.newContext();
@ -57,8 +57,8 @@ test.describe("Device activity", () => {
const page2 = await context2.newPage(); const page2 = await context2.newPage();
// Log in both sessions, then sign out of all devices from the second session. // Log in both sessions, then sign out of all devices from the second session.
await login(page1, realm); await login(page1, testBed.realm);
await login(page2, realm); await login(page2, testBed.realm);
await page2.getByTestId("accountSecurity").click(); await page2.getByTestId("accountSecurity").click();
await page2.getByTestId("account-security/device-activity").click(); await page2.getByTestId("account-security/device-activity").click();
await page2 await page2

View File

@ -12,10 +12,10 @@ const EXTERNAL_EMAIL = "external-user@keycloak.org";
test.describe("Linked accounts", () => { test.describe("Linked accounts", () => {
test("shows linked accounts", async ({ page }) => { 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. // 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("accountSecurity").click();
await page.getByTestId("account-security/linked-accounts").click(); await page.getByTestId("account-security/linked-accounts").click();
await expect(page.getByTestId("page-heading")).toHaveText( await expect(page.getByTestId("page-heading")).toHaveText(
@ -25,7 +25,7 @@ test.describe("Linked accounts", () => {
test("cannot remove the last federated identity", async ({ page }) => { test("cannot remove the last federated identity", async ({ page }) => {
// Create an 'external' realm with a user that will be used for linking. // Create an 'external' realm with a user that will be used for linking.
const externalRealm = await createTestBed({ await using externalTestBed = await createTestBed({
users: [ users: [
{ {
...DEFAULT_USER, ...DEFAULT_USER,
@ -42,15 +42,15 @@ test.describe("Linked accounts", () => {
}); });
await adminClient.clients.create({ await adminClient.clients.create({
realm: externalRealm, realm: externalTestBed.realm,
...groupsIdPClient, ...groupsIdPClient,
}); });
// Create a realm that links to the external realm as an identity provider. // 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({ await adminClient.identityProviders.create({
realm, realm: testBed.realm,
alias: "master-idp", alias: "master-idp",
providerId: "oidc", providerId: "oidc",
enabled: true, enabled: true,
@ -58,16 +58,16 @@ test.describe("Linked accounts", () => {
clientId: "groups-idp", clientId: "groups-idp",
clientSecret: "H0JaTc7VBu3HJR26vrzMxgidfJmgI5Dw", clientSecret: "H0JaTc7VBu3HJR26vrzMxgidfJmgI5Dw",
validateSignature: "false", validateSignature: "false",
tokenUrl: `${SERVER_URL}realms/${externalRealm}/protocol/openid-connect/token`, tokenUrl: `${SERVER_URL}realms/${externalTestBed.realm}/protocol/openid-connect/token`,
jwksUrl: `${SERVER_URL}realms/${externalRealm}/protocol/openid-connect/certs`, jwksUrl: `${SERVER_URL}realms/${externalTestBed.realm}/protocol/openid-connect/certs`,
issuer: `${SERVER_URL}realms/${externalRealm}`, issuer: `${SERVER_URL}realms/${externalTestBed.realm}`,
authorizationUrl: `${SERVER_URL}realms/${externalRealm}/protocol/openid-connect/auth`, authorizationUrl: `${SERVER_URL}realms/${externalTestBed.realm}/protocol/openid-connect/auth`,
logoutUrl: `${SERVER_URL}realms/${externalRealm}/protocol/openid-connect/logout`, logoutUrl: `${SERVER_URL}realms/${externalTestBed.realm}/protocol/openid-connect/logout`,
userInfoUrl: `${SERVER_URL}realms/${externalRealm}/protocol/openid-connect/userinfo`, 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 // Click the login via master-idp provider button
await loginWithIdp(page, "master-idp"); await loginWithIdp(page, "master-idp");

View File

@ -6,10 +6,10 @@ import { createTestBed } from "../support/testbed.ts";
test.describe("Signing in", () => { test.describe("Signing in", () => {
test("shows password and OTP credentials", async ({ page }) => { 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. // 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("accountSecurity").click();
await page.getByTestId("account-security/signing-in").click(); await page.getByTestId("account-security/signing-in").click();
@ -38,17 +38,17 @@ test.describe("Signing in", () => {
test("allows setting a password credential if none exists", async ({ test("allows setting a password credential if none exists", async ({
page, page,
}) => { }) => {
const realm = await createTestBed(); await using testBed = await createTestBed();
const user = await findUserByUsername(realm, DEFAULT_USER.username); const user = await findUserByUsername(testBed.realm, DEFAULT_USER.username);
// Log in and delete the password credential of the user. // 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({ const credentials = await adminClient.users.getCredentials({
realm, realm: testBed.realm,
id: user.id as string, id: user.id as string,
}); });
await adminClient.users.deleteCredential({ await adminClient.users.deleteCredential({
realm, realm: testBed.realm,
id: user.id as string, id: user.id as string,
credentialId: credentials[0].id as string, credentialId: credentials[0].id as string,
}); });

View File

@ -6,10 +6,10 @@ test.describe("Applications", () => {
test("shows a list of applications the user has access to", async ({ test("shows a list of applications the user has access to", async ({
page, page,
}) => { }) => {
const realm = await createTestBed(); await using testBed = await createTestBed();
// Log in and navigate to the applications page. // Log in and navigate to the applications page.
await login(page, realm); await login(page, testBed.realm);
await page.getByTestId("applications").click(); await page.getByTestId("applications").click();
// Assert that the applications list is displayed and contains the expected application. // Assert that the applications list is displayed and contains the expected application.

View File

@ -5,17 +5,17 @@ import { createTestBed } from "./support/testbed.ts";
test.describe("Groups", () => { test.describe("Groups", () => {
test("lists groups", async ({ page }) => { 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 page.getByTestId("groups").click();
await expect(page.getByTestId("group[1].name")).toHaveText("three"); await expect(page.getByTestId("group[1].name")).toHaveText("three");
}); });
test("lists direct and indirect groups", async ({ page }) => { 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 page.getByTestId("groups").click();
await expect( await expect(

View File

@ -7,9 +7,9 @@ import userProfileRealm from "../realms/user-profile-realm.json" with { type: "j
test.describe("Personal info", () => { test.describe("Personal info", () => {
test("sets basic information", async ({ page }) => { test("sets basic information", async ({ page }) => {
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("email").fill("edewit@somewhere.com");
await page.getByTestId("firstName").fill("Erik"); await page.getByTestId("firstName").fill("Erik");
@ -22,10 +22,13 @@ test.describe("Personal info", () => {
test.describe("Personal info (user profile enabled)", () => { test.describe("Personal info (user profile enabled)", () => {
test("renders user profile fields", async ({ page }) => { 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 adminClient.users.updateProfile({
await login(page, realm); ...userProfile,
realm: testBed.realm,
});
await login(page, testBed.realm);
await expect(page.locator("#select")).toBeVisible(); await expect(page.locator("#select")).toBeVisible();
await expect(page.getByTestId("help-label-select")).toBeVisible(); await expect(page.getByTestId("help-label-select")).toBeVisible();
@ -36,10 +39,13 @@ test.describe("Personal info (user profile enabled)", () => {
}); });
test("renders long select options as typeahead", async ({ page }) => { 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 adminClient.users.updateProfile({
await login(page, realm); ...userProfile,
realm: testBed.realm,
});
await login(page, testBed.realm);
await page.locator("#alternatelang").click(); await page.locator("#alternatelang").click();
await page.waitForSelector("text=Italiano"); await page.waitForSelector("text=Italiano");
@ -53,10 +59,13 @@ test.describe("Personal info (user profile enabled)", () => {
}); });
test("renders long list of locales as typeahead", async ({ page }) => { 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 adminClient.users.updateProfile({
await login(page, realm); ...userProfile,
realm: testBed.realm,
});
await login(page, testBed.realm);
await page.locator("#attributes\\.locale").click(); await page.locator("#attributes\\.locale").click();
await page.waitForSelector("text=Italiano"); await page.waitForSelector("text=Italiano");
@ -70,10 +79,13 @@ test.describe("Personal info (user profile enabled)", () => {
}); });
test("saves user profile", async ({ page }) => { test("saves user profile", async ({ page }) => {
const realm = await createTestBed(userProfileRealm); await using testBed = await createTestBed(userProfileRealm);
await adminClient.users.updateProfile({ ...userProfile, realm }); await adminClient.users.updateProfile({
await login(page, realm); ...userProfile,
realm: testBed.realm,
});
await login(page, testBed.realm);
await page.locator("#select").click(); await page.locator("#select").click();
await page.getByRole("option", { name: "two" }).click(); await page.getByRole("option", { name: "two" }).click();
@ -101,12 +113,12 @@ test.describe("Personal info (user profile enabled)", () => {
test.describe("Realm localization", () => { test.describe("Realm localization", () => {
test("changes locale", async ({ page }) => { test("changes locale", async ({ page }) => {
const realm = await createTestBed({ await using testBed = await createTestBed({
internationalizationEnabled: true, internationalizationEnabled: true,
supportedLocales: ["en", "nl", "de"], supportedLocales: ["en", "nl", "de"],
}); });
await login(page, realm); await login(page, testBed.realm);
await page.locator("#attributes\\.locale").click(); await page.locator("#attributes\\.locale").click();
page.getByRole("option").filter({ hasText: "Deutsch" }); page.getByRole("option").filter({ hasText: "Deutsch" });
await page.getByRole("option", { name: "English" }).click(); await page.getByRole("option", { name: "English" }).click();

View File

@ -2,26 +2,33 @@ import type RealmRepresentation from "@keycloak/keycloak-admin-client/lib/defs/r
import { expect, test } from "@playwright/test"; import { expect, test } from "@playwright/test";
import resourcesRealm from "./realms/resources-realm.json" with { type: "json" }; import resourcesRealm from "./realms/resources-realm.json" with { type: "json" };
import { login } from "./support/actions.ts"; import { login } from "./support/actions.ts";
import { createTestBed } from "./support/testbed.ts"; import { createTestBed, type TestBed } from "./support/testbed.ts";
test.describe("Resources", () => { 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" }); test.describe.configure({ mode: "serial" });
let realm: string; let testBed: TestBed;
test.beforeAll(async () => { 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 }) => { 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 page.getByTestId("resources").click();
await expect(page.getByRole("gridcell", { name: "one" })).toBeVisible(); await expect(page.getByRole("gridcell", { name: "one" })).toBeVisible();
}); });
test("shows no resources are shared with another user", async ({ page }) => { 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("resources").click();
await page.getByTestId("sharedWithMe").click(); await page.getByTestId("sharedWithMe").click();
@ -30,7 +37,7 @@ test.describe("Resources", () => {
}); });
test("shares a recourse with another user", async ({ page }) => { 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("resources").click();
await page.getByTestId("expand-one").click(); await page.getByTestId("expand-one").click();
@ -62,7 +69,7 @@ test.describe("Resources", () => {
}); });
test("shows the resources shared with another user", async ({ page }) => { 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("resources").click();
await page.getByTestId("sharedWithMe").click(); await page.getByTestId("sharedWithMe").click();

View File

@ -2,15 +2,24 @@ import type RealmRepresentation from "@keycloak/keycloak-admin-client/lib/defs/r
import { adminClient } from "./admin-client.ts"; import { adminClient } from "./admin-client.ts";
import { DEFAULT_USER } from "./common.ts"; import { DEFAULT_USER } from "./common.ts";
export interface TestBed extends AsyncDisposable {
realm: string;
}
export async function createTestBed( export async function createTestBed(
overrides?: RealmRepresentation, overrides?: RealmRepresentation,
): Promise<string> { ): Promise<TestBed> {
const { realmName } = await adminClient.realms.create({ const { realmName: realm } = await adminClient.realms.create({
enabled: true, enabled: true,
users: [DEFAULT_USER], users: [DEFAULT_USER],
...overrides, ...overrides,
realm: crypto.randomUUID(), realm: crypto.randomUUID(),
}); });
return realmName; const deleteRealm = () => adminClient.realms.del({ realm });
return {
realm,
[Symbol.asyncDispose]: deleteRealm,
};
} }

View File

@ -6,7 +6,7 @@ This project is the next generation of the Keycloak Administration UI. It is wri
### Prerequisites ### 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: You can find out which version of Node.js you are using by running the following command:

View File

@ -9,8 +9,8 @@ import { login } from "../utils/login.js";
test("OID4VCI section visibility and jump link in Tokens tab", async ({ test("OID4VCI section visibility and jump link in Tokens tab", async ({
page, page,
}) => { }) => {
const realm = await createTestBed(); await using testBed = await createTestBed();
await login(page, { to: toRealmSettings({ realm }) }); await login(page, { to: toRealmSettings({ realm: testBed.realm }) });
const tokensTab = page.getByTestId("rs-tokens-tab"); const tokensTab = page.getByTestId("rs-tokens-tab");
await tokensTab.click(); await tokensTab.click();
@ -28,8 +28,8 @@ test("OID4VCI section visibility and jump link in Tokens tab", async ({
test("should render fields and save values with correct attribute keys", async ({ test("should render fields and save values with correct attribute keys", async ({
page, page,
}) => { }) => {
const realm = await createTestBed(); await using testBed = await createTestBed();
await login(page, { to: toRealmSettings({ realm }) }); await login(page, { to: toRealmSettings({ realm: testBed.realm }) });
const tokensTab = page.getByTestId("rs-tokens-tab"); const tokensTab = page.getByTestId("rs-tokens-tab");
await tokensTab.click(); await tokensTab.click();
@ -54,7 +54,7 @@ test("should render fields and save values with correct attribute keys", async (
page.getByText("Realm successfully updated").first(), page.getByText("Realm successfully updated").first(),
).toBeVisible(); ).toBeVisible();
const realmData = await adminClient.getRealm(realm); const realmData = await adminClient.getRealm(testBed.realm);
expect(realmData).toBeDefined(); expect(realmData).toBeDefined();
// TimeSelector converts values based on selected unit (60 minutes = 3600 seconds, 120 seconds = 120 seconds) // TimeSelector converts values based on selected unit (60 minutes = 3600 seconds, 120 seconds = 120 seconds)
expect(realmData?.attributes?.["vc.c-nonce-lifetime-seconds"]).toBe("3600"); expect(realmData?.attributes?.["vc.c-nonce-lifetime-seconds"]).toBe("3600");
@ -62,8 +62,8 @@ test("should render fields and save values with correct attribute keys", async (
}); });
test("should persist values after page refresh", async ({ page }) => { test("should persist values after page refresh", async ({ page }) => {
const realm = await createTestBed(); await using testBed = await createTestBed();
await login(page, { to: toRealmSettings({ realm }) }); await login(page, { to: toRealmSettings({ realm: testBed.realm }) });
const tokensTab = page.getByTestId("rs-tokens-tab"); const tokensTab = page.getByTestId("rs-tokens-tab");
await tokensTab.click(); await tokensTab.click();
@ -89,12 +89,15 @@ test("should persist values after page refresh", async ({ page }) => {
await page.reload(); await page.reload();
// Navigate back to realm settings using the same pattern as login // Navigate back to realm settings using the same pattern as login
const url = new URL(generatePath(ROOT_PATH, { realm }), SERVER_URL); const url = new URL(
url.hash = toRealmSettings({ realm }).pathname!; generatePath(ROOT_PATH, { realm: testBed.realm }),
SERVER_URL,
);
url.hash = toRealmSettings({ realm: testBed.realm }).pathname!;
await page.goto(url.toString()); await page.goto(url.toString());
// The TimeSelector component converts values based on units, so we need to check the actual saved values // The TimeSelector component converts values based on units, so we need to check the actual saved values
const realmData = await adminClient.getRealm(realm); const realmData = await adminClient.getRealm(testBed.realm);
expect(realmData?.attributes?.["vc.c-nonce-lifetime-seconds"]).toBeDefined(); expect(realmData?.attributes?.["vc.c-nonce-lifetime-seconds"]).toBeDefined();
expect(realmData?.attributes?.["preAuthorizedCodeLifespanS"]).toBeDefined(); expect(realmData?.attributes?.["preAuthorizedCodeLifespanS"]).toBeDefined();
@ -111,8 +114,8 @@ test("should persist values after page refresh", async ({ page }) => {
}); });
test("should validate form fields and save valid values", async ({ page }) => { test("should validate form fields and save valid values", async ({ page }) => {
const realm = await createTestBed(); await using testBed = await createTestBed();
await login(page, { to: toRealmSettings({ realm }) }); await login(page, { to: toRealmSettings({ realm: testBed.realm }) });
const tokensTab = page.getByTestId("rs-tokens-tab"); const tokensTab = page.getByTestId("rs-tokens-tab");
await tokensTab.click(); await tokensTab.click();
@ -150,7 +153,7 @@ test("should validate form fields and save valid values", async ({ page }) => {
).toBeVisible(); ).toBeVisible();
// Verify the values were saved correctly // Verify the values were saved correctly
const realmData = await adminClient.getRealm(realm); const realmData = await adminClient.getRealm(testBed.realm);
expect(realmData?.attributes?.["vc.c-nonce-lifetime-seconds"]).toBeDefined(); expect(realmData?.attributes?.["vc.c-nonce-lifetime-seconds"]).toBeDefined();
expect(realmData?.attributes?.["preAuthorizedCodeLifespanS"]).toBeDefined(); expect(realmData?.attributes?.["preAuthorizedCodeLifespanS"]).toBeDefined();
@ -169,8 +172,8 @@ test("should validate form fields and save valid values", async ({ page }) => {
test("should show validation error for values below minimum threshold", async ({ test("should show validation error for values below minimum threshold", async ({
page, page,
}) => { }) => {
const realm = await createTestBed(); await using testBed = await createTestBed();
await login(page, { to: toRealmSettings({ realm }) }); await login(page, { to: toRealmSettings({ realm: testBed.realm }) });
const tokensTab = page.getByTestId("rs-tokens-tab"); const tokensTab = page.getByTestId("rs-tokens-tab");
await tokensTab.click(); await tokensTab.click();

View File

@ -1,12 +1,22 @@
import type RealmRepresentation from "@keycloak/keycloak-admin-client/lib/defs/realmRepresentation.js"; import type RealmRepresentation from "@keycloak/keycloak-admin-client/lib/defs/realmRepresentation.js";
import adminClient from "../utils/AdminClient.ts"; import adminClient from "../utils/AdminClient.ts";
export interface TestBed extends AsyncDisposable {
realm: string;
}
export async function createTestBed( export async function createTestBed(
overrides?: RealmRepresentation, overrides?: RealmRepresentation,
): Promise<string> { ): Promise<TestBed> {
const { realmName } = await adminClient.createRealm( const { realmName: realm } = await adminClient.createRealm(
crypto.randomUUID(), crypto.randomUUID(),
overrides, overrides,
); );
return realmName;
const deleteRealm = () => adminClient.deleteRealm(realm);
return {
realm,
[Symbol.asyncDispose]: deleteRealm,
};
} }

View File

@ -34,9 +34,9 @@ import {
test.describe("User creation", () => { test.describe("User creation", () => {
test("navigates to the create user page", async ({ page }) => { test("navigates to the create user page", async ({ page }) => {
const realm = await createTestBed(); await using testBed = await createTestBed();
await login(page, { to: toUsers({ realm }) }); await login(page, { to: toUsers({ realm: testBed.realm }) });
await clickAddUserButton(page); await clickAddUserButton(page);
await expect(page).toHaveURL(/.*users\/add-user/); await expect(page).toHaveURL(/.*users\/add-user/);
@ -46,9 +46,9 @@ test.describe("User creation", () => {
}); });
test("creates a new user", async ({ page }) => { test("creates a new user", async ({ page }) => {
const realm = await createTestBed(); await using testBed = await createTestBed();
await login(page, { to: toAddUser({ realm }) }); await login(page, { to: toAddUser({ realm: testBed.realm }) });
await fillUserForm(page, { await fillUserForm(page, {
username: "test-user", username: "test-user",
@ -72,11 +72,11 @@ test.describe("User creation", () => {
}); });
test("creates a user that joins a group", async ({ page }) => { test("creates a user that joins a group", async ({ page }) => {
const realm = await createTestBed({ await using testBed = await createTestBed({
groups: [{ name: "test-group" }], groups: [{ name: "test-group" }],
}); });
await login(page, { to: toAddUser({ realm }) }); await login(page, { to: toAddUser({ realm: testBed.realm }) });
await fillUserForm(page, { username: "test-user" }); await fillUserForm(page, { username: "test-user" });
await joinGroup(page, ["test-group"]); await joinGroup(page, ["test-group"]);
@ -85,9 +85,9 @@ test.describe("User creation", () => {
}); });
test("creates a user with a password credential", async ({ page }) => { test("creates a user with a password credential", async ({ page }) => {
const realm = await createTestBed(); await using testBed = await createTestBed();
await login(page, { to: toAddUser({ realm }) }); await login(page, { to: toAddUser({ realm: testBed.realm }) });
await fillUserForm(page, { await fillUserForm(page, {
username: "test-user", username: "test-user",
@ -116,28 +116,33 @@ test.describe("Existing users", () => {
}; };
test("searches for an existing user", async ({ page }) => { test("searches for an existing user", async ({ page }) => {
const realm = await createTestBed(overrides); await using testBed = await createTestBed(overrides);
await login(page, { to: toUsers({ realm }) }); await login(page, { to: toUsers({ realm: testBed.realm }) });
await searchItem(page, placeHolder, existingUserName); await searchItem(page, placeHolder, existingUserName);
await assertRowExists(page, existingUserName); await assertRowExists(page, existingUserName);
}); });
test("searches for a non-existing user", async ({ page }) => { test("searches for a non-existing user", async ({ page }) => {
const realm = await createTestBed(overrides); await using testBed = await createTestBed(overrides);
await login(page, { to: toUsers({ realm }) }); await login(page, { to: toUsers({ realm: testBed.realm }) });
await searchItem(page, "Search", "non-existing-user"); await searchItem(page, "Search", "non-existing-user");
await assertNoResults(page); await assertNoResults(page);
}); });
test("edits a user", async ({ page }) => { test("edits a user", async ({ page }) => {
const realm = await createTestBed(overrides); await using testBed = await createTestBed(overrides);
const user = await adminClient.findUserByUsername(realm, existingUserName); const user = await adminClient.findUserByUsername(
testBed.realm,
existingUserName,
);
await login(page, { to: toUser({ realm, id: user.id!, tab: "settings" }) }); await login(page, {
to: toUser({ realm: testBed.realm, id: user.id!, tab: "settings" }),
});
await fillUserForm(page, { await fillUserForm(page, {
email: "test-user@example.com", email: "test-user@example.com",
@ -151,9 +156,9 @@ test.describe("Existing users", () => {
const attributesName = "unmanagedAttributes"; const attributesName = "unmanagedAttributes";
test("adds unmanaged attributes to a user", async ({ page }) => { test("adds unmanaged attributes to a user", async ({ page }) => {
const realm = await createTestBed(overrides); await using testBed = await createTestBed(overrides);
await login(page, { to: toRealmSettings({ realm }) }); await login(page, { to: toRealmSettings({ realm: testBed.realm }) });
await selectItem(page, "#unmanagedAttributePolicy", "Enabled"); await selectItem(page, "#unmanagedAttributePolicy", "Enabled");
await page.getByTestId("realmSettingsGeneralTab-save").click(); await page.getByTestId("realmSettingsGeneralTab-save").click();
@ -185,9 +190,9 @@ test.describe("Existing users", () => {
test("adds unmanaged attributes with multiple values to a user", async ({ test("adds unmanaged attributes with multiple values to a user", async ({
page, page,
}) => { }) => {
const realm = await createTestBed(overrides); await using testBed = await createTestBed(overrides);
await login(page, { to: toRealmSettings({ realm }) }); await login(page, { to: toRealmSettings({ realm: testBed.realm }) });
await selectItem(page, "#unmanagedAttributePolicy", "Enabled"); await selectItem(page, "#unmanagedAttributePolicy", "Enabled");
await page.getByTestId("realmSettingsGeneralTab-save").click(); await page.getByTestId("realmSettingsGeneralTab-save").click();
@ -204,13 +209,18 @@ test.describe("Existing users", () => {
}); });
test("adds a user to a group", async ({ page }) => { test("adds a user to a group", async ({ page }) => {
const realm = await createTestBed({ await using testBed = await createTestBed({
...overrides, ...overrides,
groups: [{ name: "test-group" }], groups: [{ name: "test-group" }],
}); });
const user = await adminClient.findUserByUsername(realm, existingUserName); const user = await adminClient.findUserByUsername(
testBed.realm,
existingUserName,
);
await login(page, { to: toUser({ realm, id: user.id!, tab: "groups" }) }); await login(page, {
to: toUser({ realm: testBed.realm, id: user.id!, tab: "groups" }),
});
await joinGroup(page, ["test-group"], true); await joinGroup(page, ["test-group"], true);
await assertNotificationMessage(page, "Added group membership"); await assertNotificationMessage(page, "Added group membership");

View File

@ -24,7 +24,7 @@
</modules> </modules>
<properties> <properties>
<node.version>v22.18.0</node.version> <node.version>v24.9.0</node.version>
<pnpm.version>10.14.0</pnpm.version> <pnpm.version>10.14.0</pnpm.version>
<!-- The JavaScript projects use non-standard folders for their sources, therefore, name it here explicitly --> <!-- The JavaScript projects use non-standard folders for their sources, therefore, name it here explicitly -->
<maven.build.cache.input.1>src</maven.build.cache.input.1> <maven.build.cache.input.1>src</maven.build.cache.input.1>