v4: a user from another instance can't be renamed from their profile's manage tab — it offered the name field to every user, and the API refuses a remote one's rename with a 400, since their home names them. The tab now only shows their name, as the settings do for one's own.
The profile's and the settings' tests of a remote account's links to its home still expected the home's host itself, which 8bcc005 replaced with GET /api/users/:key/home; they now expect that.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -13,6 +13,7 @@ import type { PublicUser } from "../../../shared/user.ts";
|
|||||||
import { expectOk } from "../../utils/shared/api.ts";
|
import { expectOk } from "../../utils/shared/api.ts";
|
||||||
import { deserializePublicUser } from "../../utils/shared/deserialize.ts";
|
import { deserializePublicUser } from "../../utils/shared/deserialize.ts";
|
||||||
import { useAuth } from "../../hooks/auth/useAuth.ts";
|
import { useAuth } from "../../hooks/auth/useAuth.ts";
|
||||||
|
import { homeHost } from "../../utils/auth/home.ts";
|
||||||
import { ProfileSection } from "./ProfileSection.tsx";
|
import { ProfileSection } from "./ProfileSection.tsx";
|
||||||
import { RoleSwitcher } from "./RoleSwitcher.tsx";
|
import { RoleSwitcher } from "./RoleSwitcher.tsx";
|
||||||
import { SettingRow } from "./SettingRow.tsx";
|
import { SettingRow } from "./SettingRow.tsx";
|
||||||
@@ -30,7 +31,8 @@ export interface ProfileManageTabProps {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Renders the user's role and name. A rename here has no rate limit, and the
|
* Renders the user's role and name. A rename here has no rate limit, and the
|
||||||
* profile's URL then takes the new name.
|
* profile's URL then takes the new name. A user from another instance is
|
||||||
|
* named there, so their name is only shown.
|
||||||
*/
|
*/
|
||||||
export function ProfileManageTab(
|
export function ProfileManageTab(
|
||||||
{ user, onUserSaved }: ProfileManageTabProps,
|
{ user, onUserSaved }: ProfileManageTabProps,
|
||||||
@@ -59,11 +61,15 @@ export function ProfileManageTab(
|
|||||||
onSaved={(role) => onUserSaved({ ...user, role })}
|
onSaved={(role) => onUserSaved({ ...user, role })}
|
||||||
/>
|
/>
|
||||||
<SettingRow label={<Trans>Username</Trans>}>
|
<SettingRow label={<Trans>Username</Trans>}>
|
||||||
<UsernameField
|
{homeHost(user.username)
|
||||||
key={user.username}
|
? <span className="profile-appearance-value">{user.username}</span>
|
||||||
username={user.username}
|
: (
|
||||||
save={rename}
|
<UsernameField
|
||||||
/>
|
key={user.username}
|
||||||
|
username={user.username}
|
||||||
|
save={rename}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</SettingRow>
|
</SettingRow>
|
||||||
</div>
|
</div>
|
||||||
</ProfileSection>
|
</ProfileSection>
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import { calls, fail, route } from "../fetch.ts";
|
|||||||
import { makeSession, makeToken } from "../fixtures.ts";
|
import { makeSession, makeToken } from "../fixtures.ts";
|
||||||
import { renderApp } from "../render.tsx";
|
import { renderApp } from "../render.tsx";
|
||||||
import { AUTH_STORAGE_KEY } from "../../utils/auth/storage.ts";
|
import { AUTH_STORAGE_KEY } from "../../utils/auth/storage.ts";
|
||||||
|
import { API_URL } from "../../config/api.ts";
|
||||||
|
|
||||||
const instance = vi.hoisted(() => ({
|
const instance = vi.hoisted(() => ({
|
||||||
emailEnabled: false,
|
emailEnabled: false,
|
||||||
@@ -217,9 +218,10 @@ describe("Settings", () => {
|
|||||||
const link = await screen.findByRole("link", {
|
const link = await screen.findByRole("link", {
|
||||||
name: "Change it on home.example",
|
name: "Change it on home.example",
|
||||||
});
|
});
|
||||||
|
// Through the API, which redirects to the home's pages.
|
||||||
expect(link).toHaveAttribute(
|
expect(link).toHaveAttribute(
|
||||||
"href",
|
"href",
|
||||||
`${location.protocol}//home.example/settings`,
|
`${API_URL}/api/users/@alice%40home.example/home?path=%2Fsettings`,
|
||||||
);
|
);
|
||||||
expect(screen.queryByText("Other instances")).not.toBeInTheDocument();
|
expect(screen.queryByText("Other instances")).not.toBeInTheDocument();
|
||||||
expect(calls("GET /api/me/grants")).toHaveLength(0);
|
expect(calls("GET /api/me/grants")).toHaveLength(0);
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { API_URL } from "../../config/api.ts";
|
||||||
import { describe, expect, test, vi } from "vitest";
|
import { describe, expect, test, vi } from "vitest";
|
||||||
import { act, screen, waitFor, within } from "@testing-library/react";
|
import { act, screen, waitFor, within } from "@testing-library/react";
|
||||||
import { userEvent } from "@testing-library/user-event";
|
import { userEvent } from "@testing-library/user-event";
|
||||||
@@ -65,9 +66,10 @@ describe("UserPublicProfile", () => {
|
|||||||
});
|
});
|
||||||
expect(await screen.findByRole("heading", { name: "alice@home.example" }))
|
expect(await screen.findByRole("heading", { name: "alice@home.example" }))
|
||||||
.toBeInTheDocument();
|
.toBeInTheDocument();
|
||||||
|
// Through the API, which redirects to the home's pages.
|
||||||
expect(screen.getByRole("link", { name: "home.example" })).toHaveAttribute(
|
expect(screen.getByRole("link", { name: "home.example" })).toHaveAttribute(
|
||||||
"href",
|
"href",
|
||||||
`${location.protocol}//home.example/@alice`,
|
`${API_URL}/api/users/@alice%40home.example/home`,
|
||||||
);
|
);
|
||||||
expect(screen.queryByTitle("Change avatar")).not.toBeInTheDocument();
|
expect(screen.queryByTitle("Change avatar")).not.toBeInTheDocument();
|
||||||
expect(screen.queryByTitle("Edit email")).not.toBeInTheDocument();
|
expect(screen.queryByTitle("Edit email")).not.toBeInTheDocument();
|
||||||
@@ -617,6 +619,20 @@ describe("UserPublicProfile", () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("a user from another instance is named there, not here", async () => {
|
||||||
|
open({
|
||||||
|
session: makeSession({ id: "admin", role: "admin" }),
|
||||||
|
user: { ...ann, username: "ann@home.example" },
|
||||||
|
url: "/@ann@home.example/manage",
|
||||||
|
});
|
||||||
|
expect(
|
||||||
|
await screen.findByText("ann@home.example", {
|
||||||
|
selector: ".profile-appearance-value",
|
||||||
|
}),
|
||||||
|
).toBeInTheDocument();
|
||||||
|
expect(screen.queryByRole("textbox", { name: "Username" })).toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
test("non-admins get no manage tab, nor its page", async () => {
|
test("non-admins get no manage tab, nor its page", async () => {
|
||||||
open({
|
open({
|
||||||
session: makeSession({ id: "mod", role: "moderator" }),
|
session: makeSession({ id: "mod", role: "moderator" }),
|
||||||
|
|||||||
Reference in New Issue
Block a user