Parallelize client scope tests for the admin console (#43675)

Closes #43379

Signed-off-by: Jon Koops <jonkoops@gmail.com>
This commit is contained in:
Jon Koops 2025-10-24 22:08:33 +02:00 committed by GitHub
parent 84a3c29f2b
commit ee29c72ed6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 234 additions and 357 deletions

View File

@ -1,11 +1,11 @@
import { expect, test } from "@playwright/test";
import { v4 as uuidv4 } from "uuid";
import adminClient from "../utils/AdminClient.ts";
import { toClientScopes } from "../../src/client-scopes/routes/ClientScopes.tsx";
import { toNewClientScope } from "../../src/client-scopes/routes/NewClientScope.tsx";
import { createTestBed } from "../support/testbed.ts";
import { assertSaveButtonIsDisabled, clickSaveButton } from "../utils/form.ts";
import { login } from "../utils/login.ts";
import { login, navigateTo } from "../utils/login.ts";
import { assertNotificationMessage } from "../utils/masthead.ts";
import { confirmModal } from "../utils/modal.ts";
import { goToClientScopes } from "../utils/sidebar.ts";
import {
assertRowExists,
assertTableRowsLength,
@ -21,7 +21,6 @@ import {
fillClientScopeData,
getTableAssignedTypeColumn,
getTableProtocolColumn,
goToCreateItem,
selectChangeType,
selectClientScopeFilter,
selectSecondaryFilterAssignedType,
@ -42,193 +41,178 @@ const FilterProtocol = {
OpenID: "OpenID Connect",
};
test.describe.serial("Client Scopes test", () => {
const clientScopeName = "client-scope-test";
const itemId = `client-scope-test-${uuidv4()}`;
const clientScope = {
name: clientScopeName,
description: "",
protocol: "openid-connect",
attributes: {
"include.in.token.scope": "true",
"display.on.consent.screen": "true",
"gui.order": "1",
"consent.screen.text": "",
},
};
const placeHolder = "Search for client scope";
const tableName = "Client scopes";
const placeHolder = "Search for client scope";
const tableName = "Client scopes";
test.beforeAll(async () => {
for (let i = 0; i < 5; i++) {
clientScope.name = clientScopeName + i;
await adminClient.createClientScope(clientScope);
}
test.describe("Client scopes filtering", () => {
test("filters item by name", async ({ page }) => {
await using testBed = await createTestBed({
clientScopes: [{ name: "test-scope" }],
});
await login(page, { to: toClientScopes({ realm: testBed.realm }) });
await searchItem(page, placeHolder, "test-scope");
await assertRowExists(page, "test-scope");
await assertTableRowsLength(page, tableName, 1);
});
test.afterAll(async () => {
for (let i = 0; i < 5; i++) {
if (await adminClient.existsClientScope(clientScopeName + i)) {
await adminClient.deleteClientScope(clientScopeName + i);
}
}
test("filters items by assigned type 'default'", async ({ page }) => {
await using testBed = await createTestBed();
await login(page, { to: toClientScopes({ realm: testBed.realm }) });
await selectClientScopeFilter(page, "Assigned type");
await selectSecondaryFilterAssignedType(page, FilterAssignedType.Default);
const assignedTypes = await getTableAssignedTypeColumn(page, tableName);
expect(assignedTypes).toContain(FilterAssignedType.Default);
expect(assignedTypes).not.toContain(FilterAssignedType.Optional);
expect(assignedTypes).not.toContain(FilterAssignedType.None);
});
test.describe.serial("Client Scope filter list items", () => {
test.beforeEach(async ({ page }) => {
await login(page);
await goToClientScopes(page);
});
test("filters items by assigned type 'optional'", async ({ page }) => {
await using testBed = await createTestBed();
test("should filter item by name", async ({ page }) => {
const itemName = clientScopeName + "0";
await searchItem(page, placeHolder, itemName);
await assertRowExists(page, itemName);
await assertTableRowsLength(page, tableName, 1);
});
await login(page, { to: toClientScopes({ realm: testBed.realm }) });
test("should filter items by Assigned type Default", async ({ page }) => {
await selectClientScopeFilter(page, "Assigned type");
await selectSecondaryFilterAssignedType(page, FilterAssignedType.Default);
await selectClientScopeFilter(page, "Assigned type");
await selectSecondaryFilterAssignedType(page, FilterAssignedType.Optional);
const assignedTypes = await getTableAssignedTypeColumn(page, tableName);
const assignedTypes = await getTableAssignedTypeColumn(page, tableName);
expect(assignedTypes).toContain(FilterAssignedType.Default);
expect(assignedTypes).not.toContain(FilterAssignedType.Optional);
expect(assignedTypes).not.toContain(FilterAssignedType.None);
});
test("should filter items by Assigned type Optional", async ({ page }) => {
await selectClientScopeFilter(page, "Assigned type");
await selectSecondaryFilterAssignedType(
page,
FilterAssignedType.Optional,
);
const assignedTypes = await getTableAssignedTypeColumn(page, tableName);
expect(assignedTypes).not.toContain(FilterAssignedType.Default);
expect(assignedTypes).not.toContain(FilterAssignedType.None);
expect(assignedTypes).toContain(FilterAssignedType.Optional);
});
test("should filter items by Assigned type All", async ({ page }) => {
await selectClientScopeFilter(page, "Assigned type");
await selectSecondaryFilterAssignedType(
page,
FilterAssignedType.AllTypes,
);
const assignedTypes = await getTableAssignedTypeColumn(page, tableName);
expect(assignedTypes).toContain(FilterAssignedType.Default);
expect(assignedTypes).toContain(FilterAssignedType.Optional);
expect(assignedTypes).toContain(FilterAssignedType.None);
});
test("should filter items by Protocol OpenID", async ({ page }) => {
await selectClientScopeFilter(page, "Protocol");
await selectSecondaryFilterProtocol(page, FilterProtocol.OpenID);
const protocols = await getTableProtocolColumn(page, tableName);
expect(protocols).not.toContain(FilterProtocol.SAML);
expect(protocols).toContain(FilterProtocol.OpenID);
});
test("should filter items by Protocol SAML", async ({ page }) => {
await selectClientScopeFilter(page, "Protocol");
await selectSecondaryFilterProtocol(page, FilterProtocol.SAML);
const protocols = await getTableProtocolColumn(page, tableName);
expect(protocols).toContain(FilterProtocol.SAML);
expect(protocols).not.toContain(FilterProtocol.OpenID);
});
test("should show items on next page are more than 11", async ({
page,
}) => {
await clickNextPageButton(page);
const rows = await getTableData(page, tableName);
expect(rows.length).toBeGreaterThan(1);
});
expect(assignedTypes).not.toContain(FilterAssignedType.Default);
expect(assignedTypes).not.toContain(FilterAssignedType.None);
expect(assignedTypes).toContain(FilterAssignedType.Optional);
});
test.describe.serial("Client Scope modify list items", () => {
test.beforeEach(async ({ page }) => {
await login(page);
await goToClientScopes(page);
});
test("filters items by assigned type 'all'", async ({ page }) => {
await using testBed = await createTestBed();
test("should modify selected item type to Default", async ({ page }) => {
const itemName = clientScopeName + "0";
await clickSelectRow(page, tableName, itemName);
await selectChangeType(page, FilterAssignedType.Default);
await assertNotificationMessage(page, "Scope mapping updated");
await login(page, { to: toClientScopes({ realm: testBed.realm }) });
const rows = await getTableData(page, tableName);
const itemRow = rows.find((r) => r.includes(itemName));
expect(itemRow).toContain(FilterAssignedType.Default);
});
await selectClientScopeFilter(page, "Assigned type");
await selectSecondaryFilterAssignedType(page, FilterAssignedType.AllTypes);
const assignedTypes = await getTableAssignedTypeColumn(page, tableName);
expect(assignedTypes).toContain(FilterAssignedType.Default);
expect(assignedTypes).toContain(FilterAssignedType.Optional);
});
test.describe.serial("Client Scope creation", () => {
test.beforeEach(async ({ page }) => {
await login(page);
await goToClientScopes(page);
});
test("filters items by protocol 'openid'", async ({ page }) => {
await using testBed = await createTestBed();
test("should fail creating client scope", async ({ page }) => {
await goToCreateItem(page);
await assertSaveButtonIsDisabled(page);
await login(page, { to: toClientScopes({ realm: testBed.realm }) });
await fillClientScopeData(page, "address");
await page.getByTestId("save").click();
await selectClientScopeFilter(page, "Protocol");
await selectSecondaryFilterProtocol(page, FilterProtocol.OpenID);
await assertNotificationMessage(
page,
"Could not create client scope: 'Client Scope address already exists'",
);
const protocols = await getTableProtocolColumn(page, tableName);
await fillClientScopeData(page, "");
await expect(page.getByTestId("save")).toBeDisabled();
});
expect(protocols).not.toContain(FilterProtocol.SAML);
expect(protocols).toContain(FilterProtocol.OpenID);
});
test("hides 'consent text' field when 'display consent' switch is disabled", async ({
page,
}) => {
await goToCreateItem(page);
test("filters items by protocol 'saml'", async ({ page }) => {
await using testBed = await createTestBed();
await assertSwitchDisplayOnConsentScreenIsChecked(page);
await assertConsentInputIsVisible(page);
await login(page, { to: toClientScopes({ realm: testBed.realm }) });
await switchOffDisplayOnConsentScreen(page);
await selectClientScopeFilter(page, "Protocol");
await selectSecondaryFilterProtocol(page, FilterProtocol.SAML);
await assertConsentInputIsVisible(page, true);
});
const protocols = await getTableProtocolColumn(page, tableName);
test("Client scope CRUD test", async ({ page }) => {
await assertRowExists(page, itemId, false);
await goToCreateItem(page);
expect(protocols).toContain(FilterProtocol.SAML);
expect(protocols).not.toContain(FilterProtocol.OpenID);
});
await fillClientScopeData(page, itemId);
await clickSaveButton(page);
test("shows items on next page are more than 11", async ({ page }) => {
await using testBed = await createTestBed();
await assertNotificationMessage(page, "Client scope created");
await login(page, { to: toClientScopes({ realm: testBed.realm }) });
await goToClientScopes(page);
await searchItem(page, placeHolder, itemId);
await assertRowExists(page, itemId);
await clickRowKebabItem(page, itemId, "Delete");
await confirmModal(page);
await assertNotificationMessage(
page,
"The client scope has been deleted",
);
await assertRowExists(page, itemId, false);
});
await clickNextPageButton(page);
const rows = await getTableData(page, tableName);
expect(rows.length).toBeGreaterThan(1);
});
});
test.describe("Client scopes modification", () => {
test("modifies selected item type to 'default'", async ({ page }) => {
await using testBed = await createTestBed({
clientScopes: [{ name: "test-scope" }],
});
await login(page, { to: toClientScopes({ realm: testBed.realm }) });
await clickSelectRow(page, tableName, "test-scope");
await selectChangeType(page, FilterAssignedType.Default);
await assertNotificationMessage(page, "Scope mapping updated");
const rows = await getTableData(page, tableName);
const itemRow = rows.find((r) => r.includes("test-scope"));
expect(itemRow).toContain(FilterAssignedType.Default);
});
});
test.describe("Client scopes creation", () => {
test("fails creating client scope with an existing name", async ({
page,
}) => {
await using testBed = await createTestBed();
await login(page, { to: toNewClientScope({ realm: testBed.realm }) });
await assertSaveButtonIsDisabled(page);
await fillClientScopeData(page, "address");
await page.getByTestId("save").click();
await assertNotificationMessage(
page,
"Could not create client scope: 'Client Scope address already exists'",
);
await fillClientScopeData(page, "");
await expect(page.getByTestId("save")).toBeDisabled();
});
test("hides 'consent text' field when 'display consent' switch is disabled", async ({
page,
}) => {
await using testBed = await createTestBed();
await login(page, { to: toNewClientScope({ realm: testBed.realm }) });
await assertSwitchDisplayOnConsentScreenIsChecked(page);
await assertConsentInputIsVisible(page);
await switchOffDisplayOnConsentScreen(page);
await assertConsentInputIsVisible(page, true);
});
test("creates and deletes a client scope", async ({ page }) => {
const itemId = "test-scope";
await using testBed = await createTestBed();
await login(page, { to: toNewClientScope({ realm: testBed.realm }) });
await fillClientScopeData(page, itemId);
await clickSaveButton(page);
await assertNotificationMessage(page, "Client scope created");
await navigateTo(page, toClientScopes({ realm: testBed.realm }));
await searchItem(page, placeHolder, itemId);
await assertRowExists(page, itemId);
await clickRowKebabItem(page, itemId, "Delete");
await confirmModal(page);
await assertNotificationMessage(page, "The client scope has been deleted");
await assertRowExists(page, itemId, false);
});
});

View File

@ -1,9 +1,10 @@
import type RealmRepresentation from "@keycloak/keycloak-admin-client/lib/defs/realmRepresentation.js";
import { test } from "@playwright/test";
import { v4 as uuidv4 } from "uuid";
import { toClientScopes } from "../../src/client-scopes/routes/ClientScopes.tsx";
import { createTestBed } from "../support/testbed.ts";
import { clickCancelButton, clickSaveButton } from "../utils/form.ts";
import { login } from "../utils/login.ts";
import { login, navigateTo } from "../utils/login.ts";
import { assertNotificationMessage } from "../utils/masthead.ts";
import { goToClientScopes } from "../utils/sidebar.ts";
import {
assertRowExists,
clickTableRowItem,
@ -18,40 +19,37 @@ import {
goToMappersTab,
removeMappers,
} from "./mappers.ts";
import adminClient from "../utils/AdminClient.ts";
test.describe.serial("Mappers tab test", () => {
test.describe("Mappers tab", () => {
const placeHolderClientScope = "Search for client scope";
const placeHolder = "Search for mapper";
const testBedData: RealmRepresentation = {
clientScopes: [
{
name: "test-scope",
protocol: "openid-connect",
protocolMappers: [
{
name: "test-mapper",
protocol: "openid-connect",
protocolMapper: "oidc-hardcoded-claim-mapper",
},
],
},
],
};
const scopeName = `client-scope-mapper-${uuidv4()}`;
test.beforeAll(async () => {
const { id } = await adminClient.createClientScope({
name: scopeName,
description: "",
protocol: "openid-connect",
});
await adminClient.addMapping(id, {
name: "test",
protocol: "openid-connect",
protocolMapper: "oidc-hardcoded-claim-mapper",
});
});
test.afterAll(() => adminClient.deleteClientScope(scopeName));
test.beforeEach(async ({ page }) => {
await login(page);
await goToClientScopes(page);
await searchItem(page, placeHolderClientScope, scopeName);
await clickTableRowItem(page, scopeName);
await goToMappersTab(page);
});
test("CRUD predefined mappers", async ({ page }) => {
test("updates a predefined mapper", async ({ page }) => {
const placeHolder = "Search for mapper";
const mappers = ["birthdate", "email", "family name"];
await using testBed = await createTestBed(testBedData);
await login(page, { to: toClientScopes({ realm: testBed.realm }) });
await searchItem(page, placeHolderClientScope, "test-scope");
await clickTableRowItem(page, "test-scope");
await goToMappersTab(page);
await addPredefinedMappers(page, mappers);
// Configure first mapper
@ -67,9 +65,9 @@ test.describe.serial("Mappers tab test", () => {
await clickSaveButton(page);
await assertNotificationMessage(page, "Mapping successfully updated");
await goToClientScopes(page);
await searchItem(page, placeHolderClientScope, scopeName);
await clickTableRowItem(page, scopeName);
await navigateTo(page, toClientScopes({ realm: testBed.realm }));
await searchItem(page, placeHolderClientScope, "test-scope");
await clickTableRowItem(page, "test-scope");
await goToMappersTab(page);
await searchItem(page, placeHolder, mappers[0]);
await clickTableRowItem(page, mappers[0]);
@ -84,8 +82,17 @@ test.describe.serial("Mappers tab test", () => {
await clickCancelButton(page);
});
test("crud mappers by configuration", async ({ page }) => {
test("creates and removes mappers by configuration", async ({ page }) => {
const mapperNames = ["Pairwise subject identifier", "Allowed Web Origins"];
await using testBed = await createTestBed(testBedData);
await login(page, { to: toClientScopes({ realm: testBed.realm }) });
await searchItem(page, placeHolderClientScope, "test-scope");
await clickTableRowItem(page, "test-scope");
await goToMappersTab(page);
await addMappersByConfiguration(page, mapperNames);
await assertRowExists(page, mapperNames[0]);

View File

@ -1,6 +1,6 @@
import { expect, test } from "@playwright/test";
import { v4 as uuidv4 } from "uuid";
import adminClient from "../utils/AdminClient.ts";
import { toClientScopes } from "../../src/client-scopes/routes/ClientScopes.tsx";
import { createTestBed } from "../support/testbed.ts";
import { login } from "../utils/login.ts";
import { assertNotificationMessage } from "../utils/masthead.ts";
import { assertModalTitle, confirmModal } from "../utils/modal.ts";
@ -11,7 +11,6 @@ import {
confirmModalAssign,
pickRole,
} from "../utils/roles.ts";
import { goToClientScopes } from "../utils/sidebar.ts";
import {
assertEmptyTable,
assertRowExists,
@ -22,30 +21,37 @@ import {
} from "../utils/table.ts";
import { goToScopeTab } from "./scope.ts";
test.describe.serial("Scope tab test", () => {
const scopeName = `client-scope-mapper-${uuidv4()}`;
test.describe("Scope tab", () => {
const tableName = "Role list";
test.beforeAll(async () =>
adminClient.createClientScope({
name: scopeName,
description: "",
protocol: "openid-connect",
}),
);
test("assigns and unassigns role", async ({ page }) => {
await using testBed = await createTestBed({
clientScopes: [
{
name: "test-scope",
protocol: "openid-connect",
},
],
roles: {
realm: [
{
name: "composite-role",
composite: true,
composites: {
realm: ["offline_access"],
},
},
],
},
});
test.afterAll(() => adminClient.deleteClientScope(scopeName));
const role = "composite-role";
test.beforeEach(async ({ page }) => {
await login(page);
await goToClientScopes(page);
await searchItem(page, "Search for client scope", scopeName);
await clickTableRowItem(page, scopeName);
await login(page, { to: toClientScopes({ realm: testBed.realm }) });
await searchItem(page, "Search for client scope", "test-scope");
await clickTableRowItem(page, "test-scope");
await goToScopeTab(page);
});
test("Assign and unassign role", async ({ page }) => {
const role = "admin";
await pickRoleType(page, "roles");
await pickRole(page, role, true);
@ -55,9 +61,16 @@ test.describe.serial("Scope tab test", () => {
await assertRowExists(page, role);
// Initially, only directly assigned roles are shown (inherited roles are hidden by default)
const directRolesOnly = await getTableData(page, tableName);
expect(directRolesOnly.length).toBe(1); // Only composite-role is directly assigned
// Click to show inherited roles (the toggle reveals inherited roles from composite)
await clickHideInheritedRoles(page);
const data = await getTableData(page, tableName);
expect(data.length).toBeGreaterThan(1);
const allRoles = await getTableData(page, tableName);
expect(allRoles.length).toBe(2); // composite-role + offline_access (inherited from composite)
// Click again to hide inherited roles
await clickHideInheritedRoles(page);
await clickSelectRow(page, tableName, role);

View File

@ -1,5 +1,5 @@
import { Page, expect } from "@playwright/test";
import { goToRealm, goToRealms } from "../utils/sidebar";
import { type Page, expect } from "@playwright/test";
import { goToRealm, goToRealms } from "../utils/sidebar.ts";
function getCurrentRealmItem(page: Page) {
return page.getByTestId("currentRealm");

View File

@ -4,14 +4,12 @@ import type ClientScopeRepresentation from "@keycloak/keycloak-admin-client/lib/
import type ComponentRepresentation from "@keycloak/keycloak-admin-client/lib/defs/componentRepresentation.js";
import type OrganizationRepresentation from "@keycloak/keycloak-admin-client/lib/defs/organizationRepresentation.js";
import type PolicyRepresentation from "@keycloak/keycloak-admin-client/lib/defs/policyRepresentation.js";
import type ProtocolMapperRepresentation from "@keycloak/keycloak-admin-client/lib/defs/protocolMapperRepresentation.js";
import type RealmRepresentation from "@keycloak/keycloak-admin-client/lib/defs/realmRepresentation.js";
import type RoleRepresentation from "@keycloak/keycloak-admin-client/lib/defs/roleRepresentation.js";
import type { RoleMappingPayload } from "@keycloak/keycloak-admin-client/lib/defs/roleRepresentation.js";
import type { UserProfileConfig } from "@keycloak/keycloak-admin-client/lib/defs/userProfileMetadata.js";
import type UserRepresentation from "@keycloak/keycloak-admin-client/lib/defs/userRepresentation.js";
import type { Credentials } from "@keycloak/keycloak-admin-client/lib/utils/auth.js";
import { merge } from "lodash-es";
class AdminClient {
readonly #client = new KeycloakAdminClient({
@ -137,42 +135,11 @@ class AdminClient {
);
}
async getAdminUser() {
await this.#login();
const [user] = await this.#client.users.find({ username: "admin" });
return user;
}
async addUserToGroup(userId: string, groupId: string) {
await this.#login();
await this.#client.users.addToGroup({ id: userId, groupId });
}
async createUserInGroup(username: string, groupId: string) {
await this.#login();
const user = await this.createUser({ username, enabled: true });
await this.#client.users.addToGroup({ id: user.id!, groupId });
}
async addRealmRoleToUser(
userId: string,
roleName: string,
realmName: string = this.#client.realmName,
) {
await this.#login();
const realmRole = await this.#client.roles.findOneByName({
name: roleName,
realm: realmName,
});
await this.#client.users.addRealmRoleMappings({
id: userId,
roles: [realmRole as RoleMappingPayload],
realm: realmName,
});
}
async addClientRoleToUser(
userId: string,
clientId: string,
@ -247,28 +214,6 @@ class AdminClient {
return await this.#client.clientScopes.create(scope);
}
async addMapping(id: string, mapping: ProtocolMapperRepresentation) {
await this.#login();
return this.#client.clientScopes.addProtocolMapper({ id }, mapping);
}
async deleteClientScope(clientScopeName: string) {
await this.#login();
const clientScope = await this.#client.clientScopes.findOneByName({
name: clientScopeName,
});
return await this.#client.clientScopes.del({ id: clientScope?.id! });
}
async existsClientScope(clientScopeName: string) {
await this.#login();
return (await this.#client.clientScopes.findOneByName({
name: clientScopeName,
})) == undefined
? false
: true;
}
async addDefaultClientScopeInClient(
clientScopeName: string,
clientId: string,
@ -290,27 +235,6 @@ class AdminClient {
});
}
async removeDefaultClientScopeInClient(
clientScopeName: string,
clientId: string,
) {
await this.#login();
const scope = await this.#client.clientScopes.findOneByName({
name: clientScopeName,
});
const client = await this.#client.clients.find({ clientId: clientId });
return await this.#client.clients.delDefaultClientScope({
id: client[0]?.id!,
clientScopeId: scope?.id!,
});
}
async getUserProfile(realm: string) {
await this.#login();
return await this.#client.users.getProfile({ realm });
}
async addUserProfile(realm: string, userProfile: UserProfileConfig) {
await this.#login();
const currentProfile = await this.#client.users.getProfile({ realm });
@ -324,24 +248,6 @@ class AdminClient {
});
}
async updateUserProfile(realm: string, userProfile: UserProfileConfig) {
await this.#login();
await this.#client.users.updateProfile(merge(userProfile, { realm }));
}
async addGroupToProfile(realm: string, groupName: string) {
await this.#login();
const currentProfile = await this.#client.users.getProfile({ realm });
await this.#client.users.updateProfile({
...currentProfile,
realm,
...{ groups: [...currentProfile.groups!, { name: groupName }] },
});
}
async createRealmRole(payload: RoleRepresentation & { realm?: string }) {
await this.#login();
@ -433,39 +339,6 @@ class AdminClient {
});
}
async unlinkAccountIdentityProvider(
username: string,
idpDisplayName: string,
) {
await this.#login();
const user = await this.#client.users.find({ username });
const identityProviders =
(await this.#client.serverInfo.find()).identityProviders || [];
const idp = identityProviders.find(({ name }) => name === idpDisplayName);
await this.#client.users.delFromFederatedIdentity({
id: user[0].id!,
federatedIdentityId: idp?.id!,
});
}
async linkAccountIdentityProvider(username: string, idpDisplayName: string) {
await this.#login();
const user = await this.#client.users.find({ username });
const identityProviders =
(await this.#client.serverInfo.find()).identityProviders || [];
const idp = identityProviders.find(({ name }) => name === idpDisplayName);
const fedIdentity = {
identityProvider: idp?.id,
userId: "testUserIdApi",
userName: "testUserNameApi",
};
await this.#client.users.addToFederatedIdentity({
id: user[0].id!,
federatedIdentityId: idp?.id!,
federatedIdentity: fedIdentity,
});
}
async addLocalizationText(
locale: string,
key: string,

View File

@ -1,5 +1,5 @@
import type { Page } from "@playwright/test";
import { clickSelectRow } from "./table";
import { clickSelectRow } from "./table.ts";
export type RoleType = "client" | "roles";
const rolePickTableName = "Role list";

View File

@ -1,5 +1,5 @@
import type { Page } from "@playwright/test";
import { clickTableRowItem } from "./table";
import { clickTableRowItem } from "./table.ts";
export async function goToRealm(page: Page, realmName: string) {
const currentRealm = await page.getByTestId("currentRealm").textContent();