Add tests to the Account Console for user profile (#22576)
This commit is contained in:
parent
79c389f073
commit
1b722477a2
17 changed files with 397 additions and 52 deletions
2
.github/workflows/js-ci.yml
vendored
2
.github/workflows/js-ci.yml
vendored
|
@ -211,7 +211,7 @@ jobs:
|
|||
- name: Start Keycloak server
|
||||
run: |
|
||||
tar xfvz keycloak-999.0.0-SNAPSHOT.tar.gz
|
||||
keycloak-999.0.0-SNAPSHOT/bin/kc.sh start-dev &> ~/server.log &
|
||||
keycloak-999.0.0-SNAPSHOT/bin/kc.sh start-dev --features=declarative-user-profile &> ~/server.log &
|
||||
env:
|
||||
KEYCLOAK_ADMIN: admin
|
||||
KEYCLOAK_ADMIN_PASSWORD: admin
|
||||
|
|
|
@ -21,13 +21,13 @@ export default defineConfig({
|
|||
projects: [
|
||||
{ name: "setup", testMatch: /.auth\.setup\.ts/ },
|
||||
{
|
||||
name: "import test realm",
|
||||
testMatch: /test-realm\.setup\.ts/,
|
||||
teardown: "del test realm",
|
||||
name: "import realms",
|
||||
testMatch: /realm\.setup\.ts/,
|
||||
teardown: "del realms",
|
||||
},
|
||||
{
|
||||
name: "del test realm",
|
||||
testMatch: /test-realm\.teardown\.ts/,
|
||||
name: "del realms",
|
||||
testMatch: /realm\.teardown\.ts/,
|
||||
},
|
||||
{
|
||||
name: "chromium",
|
||||
|
@ -35,25 +35,15 @@ export default defineConfig({
|
|||
...devices["Desktop Chrome"],
|
||||
storageState: ".auth/user.json",
|
||||
},
|
||||
dependencies: ["setup"],
|
||||
testIgnore: ["**/*my-resources.spec.ts"],
|
||||
dependencies: ["setup", "import realms"],
|
||||
},
|
||||
|
||||
{
|
||||
name: "firefox",
|
||||
use: {
|
||||
...devices["Desktop Firefox"],
|
||||
storageState: ".auth/user.json",
|
||||
},
|
||||
dependencies: ["setup"],
|
||||
testIgnore: ["**/*my-resources.spec.ts"],
|
||||
},
|
||||
|
||||
{
|
||||
name: "resources",
|
||||
use: { ...devices["Desktop Chrome"] },
|
||||
dependencies: ["import test realm"],
|
||||
testMatch: ["**/*my-resources.spec.ts"],
|
||||
dependencies: ["setup", "import realms"],
|
||||
},
|
||||
],
|
||||
});
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import KeycloakAdminClient from "@keycloak/keycloak-admin-client";
|
||||
import RealmRepresentation from "@keycloak/keycloak-admin-client/lib/defs/realmRepresentation";
|
||||
import type UserProfileConfig from "@keycloak/keycloak-admin-client/lib/defs/userProfileConfig";
|
||||
|
||||
const adminClient = new KeycloakAdminClient({
|
||||
baseUrl: process.env.KEYCLOAK_SERVER || "http://127.0.0.1:8180",
|
||||
|
@ -28,3 +29,10 @@ export async function importRealm(realm: RealmRepresentation) {
|
|||
export async function deleteRealm(realm: string) {
|
||||
await adminClient.realms.del({ realm });
|
||||
}
|
||||
|
||||
export async function importUserProfile(
|
||||
userProfile: UserProfileConfig,
|
||||
realm: string,
|
||||
) {
|
||||
await adminClient.users.updateProfile({ ...userProfile, realm });
|
||||
}
|
||||
|
|
|
@ -1,13 +1,7 @@
|
|||
import { test, expect } from "@playwright/test";
|
||||
import { login } from "./login";
|
||||
import { deleteRealm, importRealm } from "./admin-client";
|
||||
import groupsRealm from "./groups-realm.json" assert { type: "json" };
|
||||
import RealmRepresentation from "@keycloak/keycloak-admin-client/lib/defs/realmRepresentation";
|
||||
|
||||
test.describe("Groups page", () => {
|
||||
test.beforeAll(() => importRealm(groupsRealm as RealmRepresentation));
|
||||
test.afterAll(() => deleteRealm("groups"));
|
||||
|
||||
test("List my groups", async ({ page }) => {
|
||||
await login(page, "jdoe", "jdoe", "groups");
|
||||
await page.getByTestId("groups").click();
|
||||
|
|
|
@ -1,14 +0,0 @@
|
|||
import { test, expect } from "@playwright/test";
|
||||
|
||||
test.describe("Personal info page", () => {
|
||||
test("sets basic information", async ({ page }) => {
|
||||
await page.goto("./");
|
||||
await page.getByTestId("email").fill("edewit@somewhere.com");
|
||||
await page.getByTestId("firstName").fill("Erik");
|
||||
await page.getByTestId("lastName").fill("de Wit");
|
||||
await page.getByTestId("save").click();
|
||||
|
||||
const alerts = page.getByTestId("alerts");
|
||||
await expect(alerts).toHaveText("Your account has been updated.");
|
||||
});
|
||||
});
|
32
js/apps/account-ui/test/personal-info/personal-info.spec.ts
Normal file
32
js/apps/account-ui/test/personal-info/personal-info.spec.ts
Normal file
|
@ -0,0 +1,32 @@
|
|||
import type UserProfileConfig from "@keycloak/keycloak-admin-client/lib/defs/userProfileConfig";
|
||||
import { expect, test } from "@playwright/test";
|
||||
import { importUserProfile } from "../admin-client";
|
||||
import { login } from "../login";
|
||||
import userProfileConfig from "./user-profile.json" assert { type: "json" };
|
||||
|
||||
test.describe("Personal info page", () => {
|
||||
test("sets basic information", async ({ page }) => {
|
||||
await page.goto("./");
|
||||
await page.getByTestId("email").fill("edewit@somewhere.com");
|
||||
await page.getByTestId("firstName").fill("Erik");
|
||||
await page.getByTestId("lastName").fill("de Wit");
|
||||
await page.getByTestId("save").click();
|
||||
|
||||
const alerts = page.getByTestId("alerts");
|
||||
await expect(alerts).toHaveText("Your account has been updated.");
|
||||
});
|
||||
});
|
||||
|
||||
test.describe("Personal info with userprofile enabled", async () => {
|
||||
const realm = "user-profile";
|
||||
|
||||
test.beforeAll(async () => {
|
||||
await importUserProfile(userProfileConfig as UserProfileConfig, realm);
|
||||
});
|
||||
|
||||
test("render user profile fields", async ({ page }) => {
|
||||
await login(page, "jdoe", "jdoe", realm);
|
||||
|
||||
await expect(page.locator("#select")).toBeVisible();
|
||||
});
|
||||
});
|
233
js/apps/account-ui/test/personal-info/user-profile.json
Normal file
233
js/apps/account-ui/test/personal-info/user-profile.json
Normal file
|
@ -0,0 +1,233 @@
|
|||
{
|
||||
"attributes": [
|
||||
{
|
||||
"name": "username",
|
||||
"displayName": "${username}",
|
||||
"permissions": {
|
||||
"view": [
|
||||
"admin",
|
||||
"user"
|
||||
],
|
||||
"edit": [
|
||||
"admin",
|
||||
"user"
|
||||
]
|
||||
},
|
||||
"validations": {
|
||||
"length": {
|
||||
"min": 3,
|
||||
"max": 255
|
||||
},
|
||||
"username-prohibited-characters": {},
|
||||
"up-username-not-idn-homograph": {}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "email",
|
||||
"displayName": "${email}",
|
||||
"required": {
|
||||
"roles": [
|
||||
"user"
|
||||
]
|
||||
},
|
||||
"permissions": {
|
||||
"view": [
|
||||
"admin",
|
||||
"user"
|
||||
],
|
||||
"edit": [
|
||||
"admin",
|
||||
"user"
|
||||
]
|
||||
},
|
||||
"validations": {
|
||||
"email": {},
|
||||
"length": {
|
||||
"max": 255
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "firstName",
|
||||
"displayName": "${firstName}",
|
||||
"required": {
|
||||
"roles": [
|
||||
"user"
|
||||
]
|
||||
},
|
||||
"permissions": {
|
||||
"view": [
|
||||
"admin",
|
||||
"user"
|
||||
],
|
||||
"edit": [
|
||||
"admin",
|
||||
"user"
|
||||
]
|
||||
},
|
||||
"validations": {
|
||||
"length": {
|
||||
"max": 255
|
||||
},
|
||||
"person-name-prohibited-characters": {}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "lastName",
|
||||
"displayName": "${lastName}",
|
||||
"required": {
|
||||
"roles": [
|
||||
"user"
|
||||
]
|
||||
},
|
||||
"permissions": {
|
||||
"view": [
|
||||
"admin",
|
||||
"user"
|
||||
],
|
||||
"edit": [
|
||||
"admin",
|
||||
"user"
|
||||
]
|
||||
},
|
||||
"validations": {
|
||||
"length": {
|
||||
"max": 255
|
||||
},
|
||||
"person-name-prohibited-characters": {}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "select",
|
||||
"displayName": "Select",
|
||||
"selector": {
|
||||
"scopes": [
|
||||
"roles",
|
||||
"offline_access",
|
||||
"role_list",
|
||||
"acr",
|
||||
"address",
|
||||
"microprofile-jwt",
|
||||
"web-origins",
|
||||
"profile",
|
||||
"phone",
|
||||
"email"
|
||||
]
|
||||
},
|
||||
"permissions": {
|
||||
"edit": [
|
||||
"user"
|
||||
],
|
||||
"view": [
|
||||
"user"
|
||||
]
|
||||
},
|
||||
"annotations": {
|
||||
"inputType": "select",
|
||||
"inputHelperTextBefore": "This is helping a lot"
|
||||
},
|
||||
"validations": {
|
||||
"options": {
|
||||
"options": [
|
||||
"one",
|
||||
"two",
|
||||
"three"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "hobbies",
|
||||
"displayName": "",
|
||||
"selector": {
|
||||
"scopes": [
|
||||
"roles",
|
||||
"offline_access",
|
||||
"role_list",
|
||||
"acr",
|
||||
"address",
|
||||
"microprofile-jwt",
|
||||
"web-origins",
|
||||
"profile",
|
||||
"phone",
|
||||
"email"
|
||||
]
|
||||
},
|
||||
"permissions": {
|
||||
"edit": [
|
||||
"user",
|
||||
"admin"
|
||||
],
|
||||
"view": [
|
||||
"user",
|
||||
"admin"
|
||||
]
|
||||
},
|
||||
"annotations": {
|
||||
"inputType": "textarea"
|
||||
},
|
||||
"validations": {},
|
||||
"group": "group"
|
||||
},
|
||||
{
|
||||
"name": "email2",
|
||||
"displayName": "Alternative email",
|
||||
"required": {
|
||||
"roles": [
|
||||
"user"
|
||||
],
|
||||
"scopes": [
|
||||
"roles",
|
||||
"offline_access",
|
||||
"role_list",
|
||||
"acr",
|
||||
"address",
|
||||
"microprofile-jwt",
|
||||
"web-origins",
|
||||
"profile",
|
||||
"phone",
|
||||
"email"
|
||||
]
|
||||
},
|
||||
"selector": {
|
||||
"scopes": [
|
||||
"roles",
|
||||
"offline_access",
|
||||
"role_list",
|
||||
"acr",
|
||||
"address",
|
||||
"microprofile-jwt",
|
||||
"web-origins",
|
||||
"profile",
|
||||
"phone",
|
||||
"email"
|
||||
]
|
||||
},
|
||||
"permissions": {
|
||||
"edit": [
|
||||
"admin",
|
||||
"user"
|
||||
],
|
||||
"view": [
|
||||
"admin",
|
||||
"user"
|
||||
]
|
||||
},
|
||||
"annotations": {
|
||||
"inputType": "html5-email"
|
||||
},
|
||||
"validations": {
|
||||
"email": {}
|
||||
},
|
||||
"group": "group"
|
||||
}
|
||||
],
|
||||
"groups": [
|
||||
{
|
||||
"annotations": {},
|
||||
"displayDescription": "We want to be able to tell what kind of person you are please fill this out",
|
||||
"displayHeader": "The Group",
|
||||
"name": "group"
|
||||
}
|
||||
]
|
||||
}
|
13
js/apps/account-ui/test/realm.setup.ts
Normal file
13
js/apps/account-ui/test/realm.setup.ts
Normal file
|
@ -0,0 +1,13 @@
|
|||
import RealmRepresentation from "@keycloak/keycloak-admin-client/lib/defs/realmRepresentation";
|
||||
import { test as setup } from "@playwright/test";
|
||||
|
||||
import { importRealm } from "./admin-client";
|
||||
import groupsRealm from "./realms/groups-realm.json" assert { type: "json" };
|
||||
import resourcesRealm from "./realms/resources-realm.json" assert { type: "json" };
|
||||
import userProfileRealm from "./realms/user-profile-realm.json" assert { type: "json" };
|
||||
|
||||
setup("import realm", async () => {
|
||||
await importRealm(groupsRealm as RealmRepresentation);
|
||||
await importRealm(resourcesRealm as RealmRepresentation);
|
||||
await importRealm(userProfileRealm as RealmRepresentation);
|
||||
});
|
8
js/apps/account-ui/test/realm.teardown.ts
Normal file
8
js/apps/account-ui/test/realm.teardown.ts
Normal file
|
@ -0,0 +1,8 @@
|
|||
import { test as setup } from "@playwright/test";
|
||||
import { deleteRealm } from "./admin-client";
|
||||
|
||||
setup("delete realm", async () => {
|
||||
await deleteRealm("photoz");
|
||||
await deleteRealm("groups");
|
||||
await deleteRealm("user-profile");
|
||||
});
|
85
js/apps/account-ui/test/realms/user-profile-realm.json
Normal file
85
js/apps/account-ui/test/realms/user-profile-realm.json
Normal file
|
@ -0,0 +1,85 @@
|
|||
{
|
||||
"realm": "user-profile",
|
||||
"accountTheme": "keycloak.v3",
|
||||
"enabled": true,
|
||||
"userManagedAccessAllowed": true,
|
||||
"sslRequired": "external",
|
||||
"privateKey": "MIICXAIBAAKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQABAoGAfmO8gVhyBxdqlxmIuglbz8bcjQbhXJLR2EoS8ngTXmN1bo2L90M0mUKSdc7qF10LgETBzqL8jYlQIbt+e6TH8fcEpKCjUlyq0Mf/vVbfZSNaVycY13nTzo27iPyWQHK5NLuJzn1xvxxrUeXI6A2WFpGEBLbHjwpx5WQG9A+2scECQQDvdn9NE75HPTVPxBqsEd2z10TKkl9CZxu10Qby3iQQmWLEJ9LNmy3acvKrE3gMiYNWb6xHPKiIqOR1as7L24aTAkEAtyvQOlCvr5kAjVqrEKXalj0Tzewjweuxc0pskvArTI2Oo070h65GpoIKLc9jf+UA69cRtquwP93aZKtW06U8dQJAF2Y44ks/mK5+eyDqik3koCI08qaC8HYq2wVl7G2QkJ6sbAaILtcvD92ToOvyGyeE0flvmDZxMYlvaZnaQ0lcSQJBAKZU6umJi3/xeEbkJqMfeLclD27XGEFoPeNrmdx0q10Azp4NfJAY+Z8KRyQCR2BEG+oNitBOZ+YXF9KCpH3cdmECQHEigJhYg+ykOvr1aiZUMFT72HU0jnmQe2FVekuG+LJUt2Tm7GtMjTFoGpf0JwrVuZN39fOYAlo+nTixgeW7X8Y=",
|
||||
"publicKey": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
|
||||
"requiredCredentials": [
|
||||
"password"
|
||||
],
|
||||
"attributes": {
|
||||
"userProfileEnabled": "true"
|
||||
},
|
||||
"users": [
|
||||
{
|
||||
"username": "jdoe",
|
||||
"enabled": true,
|
||||
"email": "jdoe@keycloak.org",
|
||||
"firstName": "John",
|
||||
"lastName": "Doe",
|
||||
"credentials": [
|
||||
{
|
||||
"type": "password",
|
||||
"value": "jdoe"
|
||||
}
|
||||
],
|
||||
"realmRoles": [],
|
||||
"clientRoles": {
|
||||
"account": [
|
||||
"manage-account"
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"clients": [
|
||||
{
|
||||
"clientId": "security-admin-console-v2",
|
||||
"rootUrl": "http://localhost:8080/",
|
||||
"adminUrl": "http://localhost:8080/",
|
||||
"surrogateAuthRequired": false,
|
||||
"enabled": true,
|
||||
"alwaysDisplayInConsole": false,
|
||||
"clientAuthenticatorType": "client-secret",
|
||||
"redirectUris": [
|
||||
"http://localhost:8080/*"
|
||||
],
|
||||
"webOrigins": [
|
||||
"http://localhost:8080"
|
||||
],
|
||||
"notBefore": 0,
|
||||
"bearerOnly": false,
|
||||
"consentRequired": false,
|
||||
"standardFlowEnabled": true,
|
||||
"implicitFlowEnabled": false,
|
||||
"directAccessGrantsEnabled": true,
|
||||
"serviceAccountsEnabled": false,
|
||||
"publicClient": true,
|
||||
"frontchannelLogout": false,
|
||||
"protocol": "openid-connect",
|
||||
"attributes": {},
|
||||
"authenticationFlowBindingOverrides": {},
|
||||
"fullScopeAllowed": true,
|
||||
"nodeReRegistrationTimeout": -1,
|
||||
"defaultClientScopes": [
|
||||
"web-origins",
|
||||
"role_list",
|
||||
"roles",
|
||||
"profile",
|
||||
"email"
|
||||
],
|
||||
"optionalClientScopes": [
|
||||
"address",
|
||||
"phone",
|
||||
"offline_access",
|
||||
"microprofile-jwt"
|
||||
],
|
||||
"access": {
|
||||
"view": true,
|
||||
"configure": true,
|
||||
"manage": true
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
|
@ -1,7 +0,0 @@
|
|||
import RealmRepresentation from "@keycloak/keycloak-admin-client/lib/defs/realmRepresentation";
|
||||
import { test as setup } from "@playwright/test";
|
||||
|
||||
import { importRealm } from "./admin-client";
|
||||
import testRealm from "./test-realm.json" assert { type: "json" };
|
||||
|
||||
setup("import realm", () => importRealm(testRealm as RealmRepresentation));
|
|
@ -1,4 +0,0 @@
|
|||
import { test as setup } from "@playwright/test";
|
||||
import { deleteRealm } from "./admin-client";
|
||||
|
||||
setup("delete realm", () => deleteRealm("photoz"));
|
|
@ -158,7 +158,7 @@ export default function NewAttributeSettings() {
|
|||
"validations",
|
||||
Object.entries(validations || {}).map(([key, value]) => ({
|
||||
key,
|
||||
value,
|
||||
value: value as Record<string, unknown>,
|
||||
})),
|
||||
);
|
||||
form.setValue("isRequired", required !== undefined);
|
||||
|
|
|
@ -11,9 +11,16 @@ export type UserProfileFieldsProps = UserProfileAttribute & {
|
|||
roles?: string[];
|
||||
};
|
||||
|
||||
type LengthValidator =
|
||||
| {
|
||||
min: number;
|
||||
}
|
||||
| undefined;
|
||||
|
||||
const isRequired = (attribute: UserProfileAttribute) =>
|
||||
Object.keys(attribute.required || {}).length !== 0 ||
|
||||
((attribute.validations?.length?.min as number) || 0) > 0;
|
||||
(((attribute.validations?.length as LengthValidator)?.min as number) || 0) >
|
||||
0;
|
||||
|
||||
export const UserProfileGroup = ({
|
||||
children,
|
||||
|
|
|
@ -7,7 +7,7 @@ export default interface UserProfileConfig {
|
|||
// See: https://github.com/keycloak/keycloak/blob/main/services/src/main/java/org/keycloak/userprofile/config/UPAttribute.java
|
||||
export interface UserProfileAttribute {
|
||||
name?: string;
|
||||
validations?: Record<string, Record<string, unknown>>;
|
||||
validations?: Record<string, unknown>;
|
||||
annotations?: Record<string, unknown>;
|
||||
required?: UserProfileAttributeRequired;
|
||||
readOnly?: boolean;
|
||||
|
|
Loading…
Reference in a new issue