mirror of
https://github.com/wassname/talk.git
synced 2026-08-01 13:00:55 +08:00
[CORL-156] Manage user suspension status (#2419)
* wire up suspension modal * show user suspended callout on stream * prevent comment actions for suspended users * set default to 3 hour suspension * add message field to suspension * allow custom message for suspension * show suspend success modal * fix type errors * remove unused code * update styles * fix fixture for streams * add suspension ui tests * fix types * remove warnings? * remove snapshot * fix merge conflicts * allow custom email message when banning users * fix typo * correct message type * use final-form in suspend modal * refactor suspend modal to use final-form * refactor ban modal to use final-form * refactor userStatusChangeContainer to use useCallback * feat: improve translated form * fix: addressed issue caused by i18n refactor * update getMessage to accept arguments, remove format method * translate suspend info * change hour format * make message a mandatory input for suspend and ban user * fix types in user table * Revert "fix types in user table" This reverts commit d396e90b88bb1bd354c5cdbdd72b6d8f1ab72929. * fix types for user table * fix: small review tweaks
This commit is contained in:
committed by
Wyatt Johnson
parent
4e548e8fbf
commit
5df2de6afc
@@ -1,120 +1,5 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`ban user 1`] = `
|
||||
<div
|
||||
aria-labelledby="banModal-title"
|
||||
className="Modal-root"
|
||||
onKeyDown={[Function]}
|
||||
role="modal"
|
||||
>
|
||||
<div
|
||||
className="Backdrop-root Backdrop-active"
|
||||
data-testid="backdrop"
|
||||
onClick={[Function]}
|
||||
/>
|
||||
<div
|
||||
className="Modal-scroll"
|
||||
>
|
||||
<div
|
||||
className="Modal-alignContainer1"
|
||||
>
|
||||
<div
|
||||
className="Modal-alignContainer2"
|
||||
>
|
||||
<div
|
||||
className="Modal-wrapper"
|
||||
>
|
||||
<div
|
||||
onFocus={[Function]}
|
||||
tabIndex={0}
|
||||
/>
|
||||
<div
|
||||
tabIndex={-1}
|
||||
/>
|
||||
<div
|
||||
className="Card-root BanModal-card"
|
||||
>
|
||||
<button
|
||||
className="BaseButton-root CloseButton-root"
|
||||
onBlur={[Function]}
|
||||
onClick={[Function]}
|
||||
onFocus={[Function]}
|
||||
onMouseOut={[Function]}
|
||||
onMouseOver={[Function]}
|
||||
onTouchEnd={[Function]}
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
aria-hidden="true"
|
||||
className="Icon-root Icon-md CloseButton-icon"
|
||||
>
|
||||
close
|
||||
</span>
|
||||
</button>
|
||||
<div
|
||||
className="Box-root HorizontalGutter-root HorizontalGutter-double"
|
||||
>
|
||||
<div
|
||||
className="Box-root HorizontalGutter-root HorizontalGutter-full"
|
||||
>
|
||||
<h1
|
||||
className="Box-root Typography-root Typography-header2 Typography-colorTextPrimary"
|
||||
id="banModal-title"
|
||||
>
|
||||
Are you sure you want to ban
|
||||
<strong>
|
||||
Isabelle
|
||||
</strong>
|
||||
?
|
||||
</h1>
|
||||
<p
|
||||
className="Box-root Typography-root Typography-bodyCopy Typography-colorTextPrimary"
|
||||
>
|
||||
Once banned, this user will no longer be able to comment, use
|
||||
reactions, or report comments.
|
||||
</p>
|
||||
</div>
|
||||
<div
|
||||
className="Box-root Flex-root Flex-flex Flex-halfItemGutter Flex-justifyFlexEnd gutter"
|
||||
>
|
||||
<button
|
||||
className="BaseButton-root Button-root Button-sizeRegular Button-colorRegular Button-variantOutlined"
|
||||
onBlur={[Function]}
|
||||
onClick={[Function]}
|
||||
onFocus={[Function]}
|
||||
onMouseOut={[Function]}
|
||||
onMouseOver={[Function]}
|
||||
onTouchEnd={[Function]}
|
||||
type="button"
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
<button
|
||||
className="BaseButton-root Button-root Button-sizeRegular Button-colorPrimary Button-variantFilled"
|
||||
onBlur={[Function]}
|
||||
onClick={[Function]}
|
||||
onFocus={[Function]}
|
||||
onMouseOut={[Function]}
|
||||
onMouseOver={[Function]}
|
||||
onTouchEnd={[Function]}
|
||||
type="button"
|
||||
>
|
||||
Ban User
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
onFocus={[Function]}
|
||||
tabIndex={0}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`renders community 1`] = `
|
||||
<div
|
||||
className="MainLayout-root Community-root"
|
||||
|
||||
@@ -404,6 +404,264 @@ it("can't change staff, moderator and admin status", async () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("suspend user", async () => {
|
||||
const user = users.commenters[0];
|
||||
|
||||
const resolvers = createResolversStub<GQLResolver>({
|
||||
Mutation: {
|
||||
suspendUser: ({ variables }) => {
|
||||
expectAndFail(variables).toMatchObject({
|
||||
userID: user.id,
|
||||
});
|
||||
const userRecord = pureMerge<typeof user>(user, {
|
||||
status: {
|
||||
current: user.status.current.concat(GQLUSER_STATUS.SUSPENDED),
|
||||
suspension: { active: true },
|
||||
},
|
||||
});
|
||||
return {
|
||||
user: userRecord,
|
||||
};
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const { container, testRenderer } = await createTestRenderer({
|
||||
resolvers,
|
||||
});
|
||||
|
||||
const userRow = within(container).getByText(user.username!, {
|
||||
selector: "tr",
|
||||
});
|
||||
|
||||
TestRenderer.act(() => {
|
||||
within(userRow)
|
||||
.getByLabelText("Change user status")
|
||||
.props.onClick();
|
||||
});
|
||||
|
||||
const popup = within(userRow).getByLabelText(
|
||||
"A dropdown to change the user status"
|
||||
);
|
||||
|
||||
TestRenderer.act(() => {
|
||||
within(popup)
|
||||
.getByText("Suspend User", { selector: "button" })
|
||||
.props.onClick();
|
||||
});
|
||||
|
||||
const modal = within(testRenderer.root).getByLabelText("Suspend", {
|
||||
exact: false,
|
||||
});
|
||||
|
||||
TestRenderer.act(() => {
|
||||
within(modal)
|
||||
.getByType("form")
|
||||
.props.onSubmit();
|
||||
});
|
||||
within(userRow).getByText("Suspended");
|
||||
expect(resolvers.Mutation!.suspendUser!.called).toBe(true);
|
||||
});
|
||||
|
||||
it("remove user suspension", async () => {
|
||||
const user = users.suspendedCommenter;
|
||||
const resolvers = createResolversStub<GQLResolver>({
|
||||
Mutation: {
|
||||
removeUserSuspension: ({ variables }) => {
|
||||
expectAndFail(variables).toMatchObject({
|
||||
userID: user.id,
|
||||
});
|
||||
const userRecord = pureMerge<typeof user>(user, {
|
||||
status: {
|
||||
current: [GQLUSER_STATUS.ACTIVE],
|
||||
suspension: { active: false },
|
||||
},
|
||||
});
|
||||
return {
|
||||
user: userRecord,
|
||||
};
|
||||
},
|
||||
},
|
||||
Query: {
|
||||
users: () => ({
|
||||
edges: [
|
||||
{
|
||||
node: user,
|
||||
cursor: user.createdAt,
|
||||
},
|
||||
],
|
||||
pageInfo: { endCursor: null, hasNextPage: false },
|
||||
}),
|
||||
},
|
||||
});
|
||||
|
||||
const { container } = await createTestRenderer({
|
||||
resolvers,
|
||||
});
|
||||
|
||||
const userRow = within(container).getByText(user.username!, {
|
||||
selector: "tr",
|
||||
});
|
||||
|
||||
TestRenderer.act(() => {
|
||||
within(userRow)
|
||||
.getByLabelText("Change user status")
|
||||
.props.onClick();
|
||||
});
|
||||
|
||||
const popup = within(userRow).getByLabelText(
|
||||
"A dropdown to change the user status"
|
||||
);
|
||||
|
||||
TestRenderer.act(() => {
|
||||
within(popup)
|
||||
.getByText("Remove Suspension", { selector: "button" })
|
||||
.props.onClick();
|
||||
});
|
||||
expect(resolvers.Mutation!.removeUserSuspension!.called).toBe(true);
|
||||
|
||||
await waitForElement(() => within(userRow).getByText("Active"));
|
||||
});
|
||||
|
||||
it("suspend user with custom timeout", async () => {
|
||||
const user = users.commenters[0];
|
||||
|
||||
const resolvers = createResolversStub<GQLResolver>({
|
||||
Mutation: {
|
||||
suspendUser: ({ variables }) => {
|
||||
expectAndFail(variables).toMatchObject({
|
||||
userID: user.id,
|
||||
timeout: 604800,
|
||||
});
|
||||
const userRecord = pureMerge<typeof user>(user, {
|
||||
status: {
|
||||
current: user.status.current.concat(GQLUSER_STATUS.SUSPENDED),
|
||||
suspension: { active: true },
|
||||
},
|
||||
});
|
||||
return {
|
||||
user: userRecord,
|
||||
};
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const { container, testRenderer } = await createTestRenderer({
|
||||
resolvers,
|
||||
});
|
||||
|
||||
const userRow = within(container).getByText(user.username!, {
|
||||
selector: "tr",
|
||||
});
|
||||
|
||||
TestRenderer.act(() => {
|
||||
within(userRow)
|
||||
.getByLabelText("Change user status")
|
||||
.props.onClick();
|
||||
});
|
||||
|
||||
const popup = within(userRow).getByLabelText(
|
||||
"A dropdown to change the user status"
|
||||
);
|
||||
|
||||
TestRenderer.act(() => {
|
||||
within(popup)
|
||||
.getByText("Suspend User", { selector: "button" })
|
||||
.props.onClick();
|
||||
});
|
||||
|
||||
const modal = within(testRenderer.root).getByLabelText("Suspend", {
|
||||
exact: false,
|
||||
});
|
||||
|
||||
const changedDuration = within(modal).getByID("duration-604800");
|
||||
|
||||
TestRenderer.act(() => {
|
||||
changedDuration.props.onChange(changedDuration.props.value.toString());
|
||||
});
|
||||
|
||||
TestRenderer.act(() => {
|
||||
within(modal)
|
||||
.getByType("form")
|
||||
.props.onSubmit();
|
||||
});
|
||||
within(userRow).getByText("Suspended");
|
||||
expect(resolvers.Mutation!.suspendUser!.called).toBe(true);
|
||||
});
|
||||
|
||||
it("suspend user with custom message", async () => {
|
||||
const user = users.commenters[0];
|
||||
|
||||
const resolvers = createResolversStub<GQLResolver>({
|
||||
Mutation: {
|
||||
suspendUser: ({ variables }) => {
|
||||
expectAndFail(variables).toMatchObject({
|
||||
userID: user.id,
|
||||
message: "YOU WERE SUSPENDED FOR BEHAVING BADLY",
|
||||
});
|
||||
const userRecord = pureMerge<typeof user>(user, {
|
||||
status: {
|
||||
current: user.status.current.concat(GQLUSER_STATUS.SUSPENDED),
|
||||
suspension: { active: true },
|
||||
},
|
||||
});
|
||||
return {
|
||||
user: userRecord,
|
||||
};
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const { container, testRenderer } = await createTestRenderer({
|
||||
resolvers,
|
||||
});
|
||||
|
||||
const userRow = within(container).getByText(user.username!, {
|
||||
selector: "tr",
|
||||
});
|
||||
|
||||
TestRenderer.act(() => {
|
||||
within(userRow)
|
||||
.getByLabelText("Change user status")
|
||||
.props.onClick();
|
||||
});
|
||||
|
||||
const popup = within(userRow).getByLabelText(
|
||||
"A dropdown to change the user status"
|
||||
);
|
||||
|
||||
TestRenderer.act(() => {
|
||||
within(popup)
|
||||
.getByText("Suspend User", { selector: "button" })
|
||||
.props.onClick();
|
||||
});
|
||||
|
||||
const modal = within(testRenderer.root).getByLabelText("Suspend", {
|
||||
exact: false,
|
||||
});
|
||||
|
||||
const toggleEdit = within(modal).getByID("suspendModal-editMessage");
|
||||
|
||||
TestRenderer.act(() => {
|
||||
toggleEdit.props.onChange(true);
|
||||
});
|
||||
|
||||
TestRenderer.act(() => {
|
||||
within(modal)
|
||||
.getByID("suspendModal-message")
|
||||
.props.onChange("YOU WERE SUSPENDED FOR BEHAVING BADLY");
|
||||
});
|
||||
|
||||
TestRenderer.act(() => {
|
||||
within(modal)
|
||||
.getByType("form")
|
||||
.props.onSubmit();
|
||||
});
|
||||
|
||||
within(userRow).getByText("Suspended");
|
||||
expect(resolvers.Mutation!.suspendUser!.called).toBe(true);
|
||||
});
|
||||
|
||||
it("ban user", async () => {
|
||||
const user = users.commenters[0];
|
||||
|
||||
@@ -457,12 +715,85 @@ it("ban user", async () => {
|
||||
}
|
||||
);
|
||||
|
||||
expect(within(modal).toJSON()).toMatchSnapshot();
|
||||
TestRenderer.act(() => {
|
||||
within(modal)
|
||||
.getByType("form")
|
||||
.props.onSubmit();
|
||||
});
|
||||
within(userRow).getByText("Banned");
|
||||
expect(resolvers.Mutation!.banUser!.called).toBe(true);
|
||||
});
|
||||
|
||||
it("ban user with custom message", async () => {
|
||||
const user = users.commenters[0];
|
||||
|
||||
const resolvers = createResolversStub<GQLResolver>({
|
||||
Mutation: {
|
||||
banUser: ({ variables }) => {
|
||||
expectAndFail(variables).toMatchObject({
|
||||
userID: user.id,
|
||||
message: "YOU WERE BANNED FOR BREAKING THE RULES",
|
||||
});
|
||||
const userRecord = pureMerge<typeof user>(user, {
|
||||
status: {
|
||||
current: user.status.current.concat(GQLUSER_STATUS.BANNED),
|
||||
ban: { active: true },
|
||||
},
|
||||
});
|
||||
return {
|
||||
user: userRecord,
|
||||
};
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const { container, testRenderer } = await createTestRenderer({
|
||||
resolvers,
|
||||
});
|
||||
|
||||
const userRow = within(container).getByText(user.username!, {
|
||||
selector: "tr",
|
||||
});
|
||||
|
||||
TestRenderer.act(() => {
|
||||
within(userRow)
|
||||
.getByLabelText("Change user status")
|
||||
.props.onClick();
|
||||
});
|
||||
|
||||
const popup = within(userRow).getByLabelText(
|
||||
"A dropdown to change the user status"
|
||||
);
|
||||
|
||||
TestRenderer.act(() => {
|
||||
within(popup)
|
||||
.getByText("Ban User", { selector: "button" })
|
||||
.props.onClick();
|
||||
});
|
||||
|
||||
const modal = within(testRenderer.root).getByLabelText(
|
||||
"Are you sure you want to ban",
|
||||
{
|
||||
exact: false,
|
||||
}
|
||||
);
|
||||
|
||||
const toggleMessage = within(modal).getByID("banModal-showMessage");
|
||||
|
||||
TestRenderer.act(() => {
|
||||
toggleMessage.props.onChange(true);
|
||||
});
|
||||
|
||||
TestRenderer.act(() => {
|
||||
within(modal)
|
||||
.getByText("Ban User")
|
||||
.props.onClick();
|
||||
.getByID("banModal-message")
|
||||
.props.onChange("YOU WERE BANNED FOR BREAKING THE RULES");
|
||||
});
|
||||
|
||||
TestRenderer.act(() => {
|
||||
within(modal)
|
||||
.getByType("form")
|
||||
.props.onSubmit();
|
||||
});
|
||||
within(userRow).getByText("Banned");
|
||||
expect(resolvers.Mutation!.banUser!.called).toBe(true);
|
||||
|
||||
@@ -355,6 +355,24 @@ export const users = {
|
||||
],
|
||||
baseUser
|
||||
),
|
||||
suspendedCommenter: createFixture<GQLUser>(
|
||||
{
|
||||
id: "user-suspended-0",
|
||||
username: "lol1111",
|
||||
email: "lol1111@test.com",
|
||||
role: GQLUSER_ROLE.COMMENTER,
|
||||
ignoreable: true,
|
||||
status: {
|
||||
current: [GQLUSER_STATUS.SUSPENDED],
|
||||
ban: { active: false },
|
||||
suspension: {
|
||||
active: true,
|
||||
until: new Date(Date.now() + 600000).toISOString(),
|
||||
},
|
||||
},
|
||||
},
|
||||
baseUser
|
||||
),
|
||||
bannedCommenter: createFixture<GQLUser>(
|
||||
{
|
||||
id: "user-banned-0",
|
||||
|
||||
Reference in New Issue
Block a user