Ensure user details form state remains in sync (#19696)

This commit is contained in:
Jon Koops 2023-04-13 15:55:50 +02:00 committed by GitHub
parent a2eb619e0e
commit b6c8344fb5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 125 additions and 5 deletions

View file

@ -44,7 +44,7 @@ describe.skip("Events tests", () => {
const result = await adminClient.createUser(
eventsTestUser.userRepresentation
);
eventsTestUser.eventsTestUserId = result.id;
eventsTestUser.eventsTestUserId = result.id!;
});
after(() =>

View file

@ -42,7 +42,7 @@ describe("Group test", () => {
enabled: true,
})
.then((user) => {
return { id: user.id, username: username + index };
return { id: user.id!, username: username + index };
});
return user;
})

View file

@ -0,0 +1,80 @@
import UserRepresentation from "@keycloak/keycloak-admin-client/lib/defs/userRepresentation";
import LoginPage from "../support/pages/LoginPage";
import Masthead from "../support/pages/admin-ui/Masthead";
import SidebarPage from "../support/pages/admin-ui/SidebarPage";
import UsersPage from "../support/pages/admin-ui/manage/users/UsersPage";
import UserDetailsPage from "../support/pages/admin-ui/manage/users/user_details/UserDetailsPage";
import adminClient from "../support/util/AdminClient";
import { keycloakBefore } from "../support/util/keycloak_hooks";
const loginPage = new LoginPage();
const sidebarPage = new SidebarPage();
const usersPage = new UsersPage();
const userDetailsPage = new UserDetailsPage();
const masthead = new Masthead();
const createUser = (fields: UserRepresentation) =>
cy
.wrap(null)
.then(() =>
adminClient.createUser({ username: crypto.randomUUID(), ...fields })
);
const deleteUser = (username: string) =>
cy.wrap(null).then(() => adminClient.deleteUser(username));
describe("User enable/disable", () => {
beforeEach(() => {
loginPage.logIn();
keycloakBefore();
sidebarPage.goToUsers();
});
it("disables a user", () => {
createUser({ enabled: true }).then(({ username }) => {
usersPage.goToUserDetailsPage(username!);
userDetailsPage.assertEnabled(username!);
userDetailsPage.toggleEnabled(username!);
masthead.checkNotificationMessage("The user has been saved");
cy.wait(1000);
userDetailsPage.assertDisabled(username!);
return deleteUser(username!);
});
});
it("enables a user", () => {
createUser({ enabled: false }).then(({ username }) => {
usersPage.goToUserDetailsPage(username!);
userDetailsPage.assertDisabled(username!);
userDetailsPage.toggleEnabled(username!);
masthead.checkNotificationMessage("The user has been saved");
cy.wait(1000);
userDetailsPage.assertEnabled(username!);
return deleteUser(username!);
});
});
// See: https://github.com/keycloak/keycloak/issues/19647
it("ensures submitting doesn't reset the enabled state", () => {
createUser({ enabled: true }).then(({ username }) => {
usersPage.goToUserDetailsPage(username!);
userDetailsPage.assertEnabled(username!);
userDetailsPage.toggleEnabled(username!);
masthead.checkNotificationMessage("The user has been saved");
cy.wait(1000);
userDetailsPage.assertDisabled(username!);
userDetailsPage.save();
masthead.checkNotificationMessage("The user has been saved");
cy.wait(1000);
userDetailsPage.assertDisabled(username!);
return deleteUser(username!);
});
});
});

View file

@ -69,4 +69,30 @@ export default class UserDetailsPage extends PageObject {
cy.findByTestId(this.sessionsTab).click();
return this;
}
toggleEnabled(userName: string) {
this.#getEnabledSwitch(userName).click({ force: true });
}
assertEnabled(userName: string) {
this.#getEnabledSwitchLabel(userName)
.scrollIntoView()
.contains("Enabled")
.should("be.visible");
}
assertDisabled(userName: string) {
this.#getEnabledSwitchLabel(userName)
.scrollIntoView()
.contains("Disabled")
.should("be.visible");
}
#getEnabledSwitch(userName: string) {
return cy.findByTestId(`${userName}-switch`);
}
#getEnabledSwitchLabel(userName: string) {
return this.#getEnabledSwitch(userName).closest("label");
}
}

View file

@ -95,7 +95,17 @@ class AdminClient {
async createUser(user: UserRepresentation) {
await this.login();
return await this.client.users.create(user);
const { id } = await this.client.users.create(user);
const createdUser = await this.client.users.findOne({ id });
if (!createdUser) {
throw new Error(
"Unable to create user, created user could not be found."
);
}
return createdUser;
}
async updateUser(id: string, payload: UserRepresentation) {

View file

@ -25,8 +25,7 @@ import { useAdminClient, useFetch } from "../context/auth/AdminClient";
import { useRealm } from "../context/realm-context/RealmContext";
import { UserProfileProvider } from "../realm-settings/user-profile/UserProfileContext";
import { useParams } from "../utils/useParams";
import { toUser, UserParams, UserTab } from "./routes/User";
import { toUsers } from "./routes/Users";
import { useUpdateEffect } from "../utils/useUpdateEffect";
import { UserAttributes } from "./UserAttributes";
import { UserConsents } from "./UserConsents";
import { UserCredentials } from "./UserCredentials";
@ -39,6 +38,8 @@ import {
} from "./UserProfileFields";
import { UserRoleMapping } from "./UserRoleMapping";
import { UserSessions } from "./UserSessions";
import { UserParams, UserTab, toUser } from "./routes/User";
import { toUsers } from "./routes/Users";
import "./user-section.css";
@ -121,6 +122,9 @@ const EditUserForm = ({ user, bruteForced, refresh }: EditUserFormProps) => {
const identityProviderLinksTab = useTab("identity-provider-links");
const sessionsTab = useTab("sessions");
// Ensure the form remains up-to-date when the user is updated.
useUpdateEffect(() => userForm.reset(user), [user]);
const save = async (formUser: UserRepresentation) => {
try {
await adminClient.users.update(