mirror of
https://github.com/wassname/talk.git
synced 2026-08-11 05:54:07 +08:00
[CORL-155] User Suspending and Banning (#2247)
* feat: suspending, banning, now propogation * feat: adapting to `now` * feat: support auth for suspension/banned * feat: added trace-id to requests * feat: new mutation api with hooks support * feat: added user status filtering, current field * feat: Implement filter by status, adapt to new USER_STATUS type, add lookup helper <3 * fix: typo * fix: tests * chore: rename banned status to ban status * test: feature test + lots of test helper improvements e.g. types * fix: add translation to ban user modal * fix: translation * fix: test
This commit is contained in:
@@ -29,7 +29,7 @@ async function createTestRenderer(
|
||||
.stub()
|
||||
.returns(
|
||||
merge(
|
||||
{ ...users[0], email: "", username: "", profiles: [] },
|
||||
{ ...users.admins[0], email: "", username: "", profiles: [] },
|
||||
get(customResolver, "Query.viewer")
|
||||
)
|
||||
),
|
||||
|
||||
@@ -25,7 +25,7 @@ async function createTestRenderer(
|
||||
settings: sinon
|
||||
.stub()
|
||||
.returns(merge({}, settings, get(customResolver, "Query.settings"))),
|
||||
viewer: sinon.stub().returns({ ...users[0], email: "" }),
|
||||
viewer: sinon.stub().returns({ ...users.admins[0], email: "" }),
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ async function createTestRenderer(
|
||||
settings: sinon
|
||||
.stub()
|
||||
.returns(merge({}, settings, get(customResolver, "Query.settings"))),
|
||||
viewer: sinon.stub().returns({ ...users[0], profiles: [] }),
|
||||
viewer: sinon.stub().returns({ ...users.admins[0], profiles: [] }),
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ const resolvers = {
|
||||
settings: sinon.stub().returns(settings),
|
||||
moderationQueues: sinon.stub().returns(emptyModerationQueues),
|
||||
comments: sinon.stub().returns(emptyRejectedComments),
|
||||
viewer: sinon.stub().returns(users[0]),
|
||||
viewer: sinon.stub().returns(users.admins[0]),
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@ function createTestRenderer(
|
||||
settings: sinon.stub().returns(settings),
|
||||
moderationQueues: sinon.stub().returns(emptyModerationQueues),
|
||||
comments: sinon.stub().returns(emptyRejectedComments),
|
||||
viewer: sinon.stub().returns({ ...users[0], ...userDiff }),
|
||||
viewer: sinon.stub().returns({ ...users.admins[0], ...userDiff }),
|
||||
},
|
||||
};
|
||||
const { testRenderer, context } = create({
|
||||
|
||||
@@ -21,7 +21,7 @@ const resolvers = {
|
||||
settings: sinon.stub().returns(settings),
|
||||
moderationQueues: sinon.stub().returns(emptyModerationQueues),
|
||||
comments: sinon.stub().returns(emptyRejectedComments),
|
||||
viewer: sinon.stub().returns(users[0]),
|
||||
viewer: sinon.stub().returns(users.admins[0]),
|
||||
},
|
||||
};
|
||||
|
||||
@@ -47,7 +47,7 @@ it("logs out", async () => {
|
||||
.returns({});
|
||||
|
||||
const userMenu = await waitForElement(() =>
|
||||
within(testRenderer.root).getByText(users[0].username, {
|
||||
within(testRenderer.root).getByText(users.admins[0].username!, {
|
||||
selector: "button",
|
||||
})
|
||||
);
|
||||
|
||||
@@ -1,5 +1,104 @@
|
||||
// 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
|
||||
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="HorizontalGutter-root HorizontalGutter-double"
|
||||
>
|
||||
<div
|
||||
className="HorizontalGutter-root HorizontalGutter-full"
|
||||
>
|
||||
<h1
|
||||
className="Typography-root Typography-header2 Typography-colorTextPrimary"
|
||||
id="banModal-title"
|
||||
>
|
||||
Are you sure you want to ban
|
||||
<strong>
|
||||
Isabelle
|
||||
</strong>
|
||||
?
|
||||
</h1>
|
||||
<p
|
||||
className="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="Flex-root Flex-flex Flex-halfItemGutter Flex-justifyFlexEnd"
|
||||
>
|
||||
<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>
|
||||
`;
|
||||
|
||||
exports[`renders community 1`] = `
|
||||
<div
|
||||
className="MainLayout-root Community-root"
|
||||
@@ -68,63 +167,111 @@ exports[`renders community 1`] = `
|
||||
>
|
||||
Show Me
|
||||
</legend>
|
||||
<span
|
||||
className="SelectField-root"
|
||||
<div
|
||||
className="Flex-root Flex-flex Flex-itemGutter"
|
||||
>
|
||||
<select
|
||||
aria-label="Search by role"
|
||||
className="SelectField-select"
|
||||
onBlur={[Function]}
|
||||
onChange={[Function]}
|
||||
onFocus={[Function]}
|
||||
value=""
|
||||
<span
|
||||
className="SelectField-root UserTableFilter-selectField"
|
||||
>
|
||||
<option
|
||||
<select
|
||||
aria-label="Search by role"
|
||||
className="SelectField-select"
|
||||
onBlur={[Function]}
|
||||
onChange={[Function]}
|
||||
onFocus={[Function]}
|
||||
value=""
|
||||
>
|
||||
Everyone
|
||||
</option>
|
||||
<optgroup
|
||||
label="Audience"
|
||||
>
|
||||
<option
|
||||
value="COMMENTER"
|
||||
value=""
|
||||
>
|
||||
Commenters
|
||||
All Roles
|
||||
</option>
|
||||
</optgroup>
|
||||
<optgroup
|
||||
label="Organization"
|
||||
>
|
||||
<option
|
||||
value="ADMIN"
|
||||
<optgroup
|
||||
label="Audience"
|
||||
>
|
||||
Admins
|
||||
</option>
|
||||
<option
|
||||
value="MODERATOR"
|
||||
<option
|
||||
value="COMMENTER"
|
||||
>
|
||||
Commenters
|
||||
</option>
|
||||
</optgroup>
|
||||
<optgroup
|
||||
label="Organization"
|
||||
>
|
||||
Moderators
|
||||
</option>
|
||||
<option
|
||||
value="STAFF"
|
||||
>
|
||||
Staff
|
||||
</option>
|
||||
</optgroup>
|
||||
</select>
|
||||
<span
|
||||
aria-hidden={true}
|
||||
className="SelectField-afterWrapper"
|
||||
>
|
||||
<option
|
||||
value="ADMIN"
|
||||
>
|
||||
Admins
|
||||
</option>
|
||||
<option
|
||||
value="MODERATOR"
|
||||
>
|
||||
Moderators
|
||||
</option>
|
||||
<option
|
||||
value="STAFF"
|
||||
>
|
||||
Staff
|
||||
</option>
|
||||
</optgroup>
|
||||
</select>
|
||||
<span
|
||||
aria-hidden="true"
|
||||
className="Icon-root Icon-sm"
|
||||
aria-hidden={true}
|
||||
className="SelectField-afterWrapper"
|
||||
>
|
||||
expand_more
|
||||
<span
|
||||
aria-hidden="true"
|
||||
className="Icon-root Icon-sm"
|
||||
>
|
||||
expand_more
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
<span
|
||||
className="SelectField-root UserTableFilter-selectField"
|
||||
>
|
||||
<select
|
||||
aria-label="Search by user status"
|
||||
className="SelectField-select"
|
||||
onBlur={[Function]}
|
||||
onChange={[Function]}
|
||||
onFocus={[Function]}
|
||||
value=""
|
||||
>
|
||||
<option
|
||||
value=""
|
||||
>
|
||||
All Statuses
|
||||
</option>
|
||||
<option
|
||||
value="ACTIVE"
|
||||
>
|
||||
Active
|
||||
</option>
|
||||
<option
|
||||
value="SUSPENDED"
|
||||
>
|
||||
Suspended
|
||||
</option>
|
||||
<option
|
||||
value="BANNED"
|
||||
>
|
||||
Banned
|
||||
</option>
|
||||
</select>
|
||||
<span
|
||||
aria-hidden={true}
|
||||
className="SelectField-afterWrapper"
|
||||
>
|
||||
<span
|
||||
aria-hidden="true"
|
||||
className="Icon-root Icon-sm"
|
||||
>
|
||||
expand_more
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
</fieldset>
|
||||
</div>
|
||||
<div
|
||||
@@ -159,6 +306,11 @@ exports[`renders community 1`] = `
|
||||
>
|
||||
Role
|
||||
</th>
|
||||
<th
|
||||
className="TableCell-root UserTable-statusColumn TableCell-header"
|
||||
>
|
||||
Status
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody
|
||||
@@ -190,11 +342,40 @@ exports[`renders community 1`] = `
|
||||
<td
|
||||
className="TableCell-root UserRow-roleColumn TableCell-body"
|
||||
>
|
||||
<span
|
||||
className="RoleText-root"
|
||||
<div
|
||||
className="ButtonPadding-root"
|
||||
>
|
||||
Admin
|
||||
</span>
|
||||
<span
|
||||
className="RoleText-root"
|
||||
>
|
||||
Admin
|
||||
</span>
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
className="TableCell-root UserRow-roleColumn TableCell-body"
|
||||
>
|
||||
<div
|
||||
className="ButtonPadding-root"
|
||||
>
|
||||
<div
|
||||
className="Typography-root Typography-detail Typography-colorSuccess"
|
||||
>
|
||||
<div
|
||||
className="Flex-root Flex-flex Flex-halfItemGutter Flex-alignCenter"
|
||||
>
|
||||
<div
|
||||
aria-hidden={true}
|
||||
className="UserStatus-dot"
|
||||
>
|
||||
•
|
||||
</div>
|
||||
<div>
|
||||
Active
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr
|
||||
@@ -264,6 +445,248 @@ exports[`renders community 1`] = `
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
className="TableCell-root UserRow-roleColumn TableCell-body"
|
||||
>
|
||||
<div
|
||||
className="ButtonPadding-root"
|
||||
>
|
||||
<div
|
||||
className="Typography-root Typography-detail Typography-colorSuccess"
|
||||
>
|
||||
<div
|
||||
className="Flex-root Flex-flex Flex-halfItemGutter Flex-alignCenter"
|
||||
>
|
||||
<div
|
||||
aria-hidden={true}
|
||||
className="UserStatus-dot"
|
||||
>
|
||||
•
|
||||
</div>
|
||||
<div>
|
||||
Active
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr
|
||||
className="TableRow-root TableRow-body"
|
||||
>
|
||||
<td
|
||||
className="TableCell-root UserRow-usernameColumn TableCell-body"
|
||||
>
|
||||
Huy
|
||||
</td>
|
||||
<td
|
||||
className="TableCell-root UserRow-emailColumn TableCell-body"
|
||||
>
|
||||
<a
|
||||
className="TextLink-root"
|
||||
href="mailto:huy@test.com"
|
||||
>
|
||||
huy@test.com
|
||||
</a>
|
||||
</td>
|
||||
<td
|
||||
className="TableCell-root UserRow-memberSinceColumn TableCell-body"
|
||||
>
|
||||
07/06/2018
|
||||
</td>
|
||||
<td
|
||||
className="TableCell-root UserRow-roleColumn TableCell-body"
|
||||
>
|
||||
<div
|
||||
className="Popover-root"
|
||||
>
|
||||
<button
|
||||
aria-label="Change role"
|
||||
className="BaseButton-root Button-root RoleChange-button Button-sizeSmall Button-colorRegular Button-variantRegular"
|
||||
onBlur={[Function]}
|
||||
onClick={[Function]}
|
||||
onFocus={[Function]}
|
||||
onMouseOut={[Function]}
|
||||
onMouseOver={[Function]}
|
||||
onTouchEnd={[Function]}
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
className="RoleText-root"
|
||||
>
|
||||
Staff
|
||||
</span>
|
||||
<span
|
||||
aria-hidden="true"
|
||||
className="Icon-root Icon-lg ButtonIcon-root"
|
||||
>
|
||||
arrow_drop_down
|
||||
</span>
|
||||
</button>
|
||||
<div
|
||||
aria-hidden={true}
|
||||
aria-labelledby="community-roleChange-ariainfo"
|
||||
id="community-roleChange"
|
||||
role="popup"
|
||||
>
|
||||
<div
|
||||
className="AriaInfo-root"
|
||||
id="community-roleChange-ariainfo"
|
||||
>
|
||||
A dropdown to change the user role
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
className="TableCell-root UserRow-roleColumn TableCell-body"
|
||||
>
|
||||
<div
|
||||
className="ButtonPadding-root"
|
||||
>
|
||||
<div
|
||||
className="Typography-root Typography-detail Typography-colorSuccess"
|
||||
>
|
||||
<div
|
||||
className="Flex-root Flex-flex Flex-halfItemGutter Flex-alignCenter"
|
||||
>
|
||||
<div
|
||||
aria-hidden={true}
|
||||
className="UserStatus-dot"
|
||||
>
|
||||
•
|
||||
</div>
|
||||
<div>
|
||||
Active
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr
|
||||
className="TableRow-root TableRow-body"
|
||||
>
|
||||
<td
|
||||
className="TableCell-root UserRow-usernameColumn TableCell-body"
|
||||
>
|
||||
Isabelle
|
||||
</td>
|
||||
<td
|
||||
className="TableCell-root UserRow-emailColumn TableCell-body"
|
||||
>
|
||||
<a
|
||||
className="TextLink-root"
|
||||
href="mailto:isabelle@test.com"
|
||||
>
|
||||
isabelle@test.com
|
||||
</a>
|
||||
</td>
|
||||
<td
|
||||
className="TableCell-root UserRow-memberSinceColumn TableCell-body"
|
||||
>
|
||||
07/06/2018
|
||||
</td>
|
||||
<td
|
||||
className="TableCell-root UserRow-roleColumn TableCell-body"
|
||||
>
|
||||
<div
|
||||
className="Popover-root"
|
||||
>
|
||||
<button
|
||||
aria-label="Change role"
|
||||
className="BaseButton-root Button-root RoleChange-button Button-sizeSmall Button-colorRegular Button-variantRegular"
|
||||
onBlur={[Function]}
|
||||
onClick={[Function]}
|
||||
onFocus={[Function]}
|
||||
onMouseOut={[Function]}
|
||||
onMouseOver={[Function]}
|
||||
onTouchEnd={[Function]}
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
className="RoleText-root RoleText-commenter"
|
||||
>
|
||||
Commenter
|
||||
</span>
|
||||
<span
|
||||
aria-hidden="true"
|
||||
className="Icon-root Icon-lg ButtonIcon-root"
|
||||
>
|
||||
arrow_drop_down
|
||||
</span>
|
||||
</button>
|
||||
<div
|
||||
aria-hidden={true}
|
||||
aria-labelledby="community-roleChange-ariainfo"
|
||||
id="community-roleChange"
|
||||
role="popup"
|
||||
>
|
||||
<div
|
||||
className="AriaInfo-root"
|
||||
id="community-roleChange-ariainfo"
|
||||
>
|
||||
A dropdown to change the user role
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
className="TableCell-root UserRow-roleColumn TableCell-body"
|
||||
>
|
||||
<div
|
||||
className="Popover-root"
|
||||
>
|
||||
<button
|
||||
aria-label="Change user status"
|
||||
className="BaseButton-root Button-root UserStatusChange-button Button-sizeSmall Button-colorRegular Button-variantRegular"
|
||||
onBlur={[Function]}
|
||||
onClick={[Function]}
|
||||
onFocus={[Function]}
|
||||
onMouseOut={[Function]}
|
||||
onMouseOver={[Function]}
|
||||
onTouchEnd={[Function]}
|
||||
type="button"
|
||||
>
|
||||
<div
|
||||
className="Typography-root Typography-detail Typography-colorSuccess"
|
||||
>
|
||||
<div
|
||||
className="Flex-root Flex-flex Flex-halfItemGutter Flex-alignCenter"
|
||||
>
|
||||
<div
|
||||
aria-hidden={true}
|
||||
className="UserStatus-dot"
|
||||
>
|
||||
•
|
||||
</div>
|
||||
<div>
|
||||
Active
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<span
|
||||
aria-hidden="true"
|
||||
className="Icon-root Icon-lg ButtonIcon-root"
|
||||
>
|
||||
arrow_drop_down
|
||||
</span>
|
||||
</button>
|
||||
<div
|
||||
aria-hidden={true}
|
||||
aria-labelledby="community-statusChange-ariainfo"
|
||||
id="community-statusChange"
|
||||
role="popup"
|
||||
>
|
||||
<div
|
||||
className="AriaInfo-root"
|
||||
id="community-statusChange-ariainfo"
|
||||
>
|
||||
A dropdown to change the user status
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
@@ -340,63 +763,111 @@ exports[`renders empty community 1`] = `
|
||||
>
|
||||
Show Me
|
||||
</legend>
|
||||
<span
|
||||
className="SelectField-root"
|
||||
<div
|
||||
className="Flex-root Flex-flex Flex-itemGutter"
|
||||
>
|
||||
<select
|
||||
aria-label="Search by role"
|
||||
className="SelectField-select"
|
||||
onBlur={[Function]}
|
||||
onChange={[Function]}
|
||||
onFocus={[Function]}
|
||||
value=""
|
||||
<span
|
||||
className="SelectField-root UserTableFilter-selectField"
|
||||
>
|
||||
<option
|
||||
<select
|
||||
aria-label="Search by role"
|
||||
className="SelectField-select"
|
||||
onBlur={[Function]}
|
||||
onChange={[Function]}
|
||||
onFocus={[Function]}
|
||||
value=""
|
||||
>
|
||||
Everyone
|
||||
</option>
|
||||
<optgroup
|
||||
label="Audience"
|
||||
>
|
||||
<option
|
||||
value="COMMENTER"
|
||||
value=""
|
||||
>
|
||||
Commenters
|
||||
All Roles
|
||||
</option>
|
||||
</optgroup>
|
||||
<optgroup
|
||||
label="Organization"
|
||||
>
|
||||
<option
|
||||
value="ADMIN"
|
||||
<optgroup
|
||||
label="Audience"
|
||||
>
|
||||
Admins
|
||||
</option>
|
||||
<option
|
||||
value="MODERATOR"
|
||||
<option
|
||||
value="COMMENTER"
|
||||
>
|
||||
Commenters
|
||||
</option>
|
||||
</optgroup>
|
||||
<optgroup
|
||||
label="Organization"
|
||||
>
|
||||
Moderators
|
||||
</option>
|
||||
<option
|
||||
value="STAFF"
|
||||
>
|
||||
Staff
|
||||
</option>
|
||||
</optgroup>
|
||||
</select>
|
||||
<span
|
||||
aria-hidden={true}
|
||||
className="SelectField-afterWrapper"
|
||||
>
|
||||
<option
|
||||
value="ADMIN"
|
||||
>
|
||||
Admins
|
||||
</option>
|
||||
<option
|
||||
value="MODERATOR"
|
||||
>
|
||||
Moderators
|
||||
</option>
|
||||
<option
|
||||
value="STAFF"
|
||||
>
|
||||
Staff
|
||||
</option>
|
||||
</optgroup>
|
||||
</select>
|
||||
<span
|
||||
aria-hidden="true"
|
||||
className="Icon-root Icon-sm"
|
||||
aria-hidden={true}
|
||||
className="SelectField-afterWrapper"
|
||||
>
|
||||
expand_more
|
||||
<span
|
||||
aria-hidden="true"
|
||||
className="Icon-root Icon-sm"
|
||||
>
|
||||
expand_more
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
<span
|
||||
className="SelectField-root UserTableFilter-selectField"
|
||||
>
|
||||
<select
|
||||
aria-label="Search by user status"
|
||||
className="SelectField-select"
|
||||
onBlur={[Function]}
|
||||
onChange={[Function]}
|
||||
onFocus={[Function]}
|
||||
value=""
|
||||
>
|
||||
<option
|
||||
value=""
|
||||
>
|
||||
All Statuses
|
||||
</option>
|
||||
<option
|
||||
value="ACTIVE"
|
||||
>
|
||||
Active
|
||||
</option>
|
||||
<option
|
||||
value="SUSPENDED"
|
||||
>
|
||||
Suspended
|
||||
</option>
|
||||
<option
|
||||
value="BANNED"
|
||||
>
|
||||
Banned
|
||||
</option>
|
||||
</select>
|
||||
<span
|
||||
aria-hidden={true}
|
||||
className="SelectField-afterWrapper"
|
||||
>
|
||||
<span
|
||||
aria-hidden="true"
|
||||
className="Icon-root Icon-sm"
|
||||
>
|
||||
expand_more
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
</fieldset>
|
||||
</div>
|
||||
<div
|
||||
@@ -431,6 +902,11 @@ exports[`renders empty community 1`] = `
|
||||
>
|
||||
Role
|
||||
</th>
|
||||
<th
|
||||
className="TableCell-root UserTable-statusColumn TableCell-header"
|
||||
>
|
||||
Status
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody
|
||||
|
||||
@@ -1,10 +1,16 @@
|
||||
import { get, merge } from "lodash";
|
||||
import { merge } from "lodash";
|
||||
import TestRenderer from "react-test-renderer";
|
||||
import sinon from "sinon";
|
||||
|
||||
import { GQLUSER_ROLE } from "talk-framework/schema";
|
||||
import {
|
||||
createSinonStub,
|
||||
GQLUSER_ROLE,
|
||||
GQLUSER_STATUS,
|
||||
QueryToSettingsResolver,
|
||||
QueryToUsersResolver,
|
||||
QueryToViewerResolver,
|
||||
} from "talk-framework/schema";
|
||||
import {
|
||||
createMutationResolverStub,
|
||||
createQueryResolverStub,
|
||||
findParentWithType,
|
||||
replaceHistoryLocation,
|
||||
waitForElement,
|
||||
@@ -12,6 +18,11 @@ import {
|
||||
within,
|
||||
} from "talk-framework/testHelpers";
|
||||
|
||||
import {
|
||||
BanUserMutation,
|
||||
RemoveUserBanMutation,
|
||||
UpdateUserRoleMutation,
|
||||
} from "talk-admin/mutations";
|
||||
import create from "../create";
|
||||
import {
|
||||
communityUsers,
|
||||
@@ -28,14 +39,16 @@ const createTestRenderer = async (resolver: any = {}) => {
|
||||
const resolvers = {
|
||||
...resolver,
|
||||
Query: {
|
||||
settings: sinon
|
||||
.stub()
|
||||
.returns(merge({}, settings, get(resolver, "Query.settings"))),
|
||||
users: sinon.stub().callsFake((_, data) => {
|
||||
expectAndFail(data.role).toBeFalsy();
|
||||
settings: createQueryResolverStub<QueryToSettingsResolver>(
|
||||
() => settings
|
||||
),
|
||||
users: createQueryResolverStub<QueryToUsersResolver>(variables => {
|
||||
expectAndFail(variables.role).toBeFalsy();
|
||||
return communityUsers;
|
||||
}),
|
||||
viewer: sinon.stub().returns(users[0]),
|
||||
viewer: createQueryResolverStub<QueryToViewerResolver>(
|
||||
() => users.admins[0]
|
||||
),
|
||||
...resolver.Query,
|
||||
},
|
||||
};
|
||||
@@ -61,7 +74,9 @@ it("renders community", async () => {
|
||||
it("renders empty community", async () => {
|
||||
const { container } = await createTestRenderer({
|
||||
Query: {
|
||||
users: sinon.stub().returns(emptyCommunityUsers),
|
||||
users: createQueryResolverStub<QueryToUsersResolver>(
|
||||
() => emptyCommunityUsers
|
||||
),
|
||||
},
|
||||
});
|
||||
expect(within(container).toJSON()).toMatchSnapshot();
|
||||
@@ -70,13 +85,16 @@ it("renders empty community", async () => {
|
||||
it("filter by role", async () => {
|
||||
const { container } = await createTestRenderer({
|
||||
Query: {
|
||||
users: createSinonStub(
|
||||
s => s.onFirstCall().returns(communityUsers),
|
||||
s =>
|
||||
s.onSecondCall().callsFake((_, data) => {
|
||||
expectAndFail(data.role).toBe(GQLUSER_ROLE.COMMENTER);
|
||||
return emptyCommunityUsers;
|
||||
})
|
||||
users: createQueryResolverStub<QueryToUsersResolver>(
|
||||
(variables, callCount) => {
|
||||
switch (callCount) {
|
||||
case 0:
|
||||
return communityUsers;
|
||||
default:
|
||||
expectAndFail(variables.role).toBe(GQLUSER_ROLE.COMMENTER);
|
||||
return emptyCommunityUsers;
|
||||
}
|
||||
}
|
||||
),
|
||||
},
|
||||
});
|
||||
@@ -98,26 +116,27 @@ it("filter by role", async () => {
|
||||
});
|
||||
|
||||
it("can't change viewer role", async () => {
|
||||
const viewer = users[0];
|
||||
const viewer = users.admins[0];
|
||||
const { container } = await createTestRenderer();
|
||||
|
||||
const viewerRow = within(container).getByText(viewer.username, {
|
||||
const viewerRow = within(container).getByText(viewer.username!, {
|
||||
selector: "tr",
|
||||
});
|
||||
expect(() => within(viewerRow).getByLabelText("Change role")).toThrow();
|
||||
});
|
||||
|
||||
it("change user role", async () => {
|
||||
const user = users[1];
|
||||
const updateUserRole = sinon.stub().callsFake((_: any, data: any) => {
|
||||
expectAndFail(data.input).toMatchObject({
|
||||
const user = users.commenters[0];
|
||||
const updateUserRole = createMutationResolverStub<
|
||||
typeof UpdateUserRoleMutation
|
||||
>(variables => {
|
||||
expectAndFail(variables).toMatchObject({
|
||||
userID: user.id,
|
||||
role: GQLUSER_ROLE.STAFF,
|
||||
});
|
||||
const userRecord = merge({}, user, { role: data.input.role });
|
||||
const userRecord = merge({}, user, { role: variables.role });
|
||||
return {
|
||||
user: userRecord,
|
||||
clientMutationId: data.input.clientMutationId,
|
||||
};
|
||||
});
|
||||
|
||||
@@ -125,7 +144,7 @@ it("change user role", async () => {
|
||||
Mutation: { updateUserRole },
|
||||
});
|
||||
|
||||
const userRow = within(container).getByText(user.username, {
|
||||
const userRow = within(container).getByText(user.username!, {
|
||||
selector: "tr",
|
||||
});
|
||||
|
||||
@@ -150,10 +169,10 @@ it("change user role", async () => {
|
||||
});
|
||||
|
||||
it("can't change role as a moderator", async () => {
|
||||
const viewer = users[1];
|
||||
const viewer = users.moderators[0];
|
||||
const { container } = await createTestRenderer({
|
||||
Query: {
|
||||
viewer: sinon.stub().returns(viewer),
|
||||
viewer: createQueryResolverStub<QueryToViewerResolver>(() => viewer),
|
||||
},
|
||||
});
|
||||
expect(() => within(container).getByLabelText("Change role")).toThrow();
|
||||
@@ -162,20 +181,38 @@ it("can't change role as a moderator", async () => {
|
||||
it("load more", async () => {
|
||||
const { container } = await createTestRenderer({
|
||||
Query: {
|
||||
users: createSinonStub(
|
||||
s =>
|
||||
s.onFirstCall().returns({
|
||||
edges: [
|
||||
{ node: users[0], cursor: users[0].createdAt },
|
||||
{ node: users[1], cursor: users[1].createdAt },
|
||||
],
|
||||
pageInfo: { endCursor: users[1].createdAt, hasNextPage: true },
|
||||
}),
|
||||
s =>
|
||||
s.onSecondCall().returns({
|
||||
edges: [{ node: users[2], cursor: users[2].createdAt }],
|
||||
pageInfo: { endCursor: users[2].createdAt, hasNextPage: false },
|
||||
})
|
||||
users: createQueryResolverStub<QueryToUsersResolver>(
|
||||
(variables, callCount) => {
|
||||
switch (callCount) {
|
||||
case 0:
|
||||
return {
|
||||
edges: [
|
||||
{ node: users.admins[0], cursor: users.admins[0].createdAt },
|
||||
{
|
||||
node: users.commenters[0],
|
||||
cursor: users.commenters[0].createdAt,
|
||||
},
|
||||
],
|
||||
pageInfo: {
|
||||
endCursor: users.commenters[0].createdAt,
|
||||
hasNextPage: true,
|
||||
},
|
||||
};
|
||||
default:
|
||||
return {
|
||||
edges: [
|
||||
{
|
||||
node: users.commenters[1],
|
||||
cursor: users.commenters[1].createdAt,
|
||||
},
|
||||
],
|
||||
pageInfo: {
|
||||
endCursor: users.commenters[1].createdAt,
|
||||
hasNextPage: false,
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
),
|
||||
},
|
||||
});
|
||||
@@ -188,19 +225,22 @@ it("load more", async () => {
|
||||
await waitUntilThrow(() => within(container).getByText("Load More"));
|
||||
|
||||
// Make sure third user was added.
|
||||
within(container).getByText(users[2].username);
|
||||
within(container).getByText(users.commenters[1].username!);
|
||||
});
|
||||
|
||||
it("filter by search", async () => {
|
||||
const { container } = await createTestRenderer({
|
||||
Query: {
|
||||
users: createSinonStub(
|
||||
s => s.onFirstCall().returns(communityUsers),
|
||||
s =>
|
||||
s.onSecondCall().callsFake((_, data) => {
|
||||
expectAndFail(data.query).toBe("search");
|
||||
return emptyCommunityUsers;
|
||||
})
|
||||
users: createQueryResolverStub<QueryToUsersResolver>(
|
||||
(variables, callCount) => {
|
||||
switch (callCount) {
|
||||
case 0:
|
||||
return communityUsers;
|
||||
default:
|
||||
expectAndFail(variables.query).toBe("search");
|
||||
return emptyCommunityUsers;
|
||||
}
|
||||
}
|
||||
),
|
||||
},
|
||||
});
|
||||
@@ -221,3 +261,170 @@ it("filter by search", async () => {
|
||||
within(container).getByText("could not find anyone", { exact: false })
|
||||
);
|
||||
});
|
||||
|
||||
it("filter by status", async () => {
|
||||
const { container } = await createTestRenderer({
|
||||
Query: {
|
||||
users: createQueryResolverStub<QueryToUsersResolver>(
|
||||
(variables, callCount) => {
|
||||
switch (callCount) {
|
||||
case 0:
|
||||
return communityUsers;
|
||||
default:
|
||||
expectAndFail(variables.status).toBe("BANNED");
|
||||
return emptyCommunityUsers;
|
||||
}
|
||||
}
|
||||
),
|
||||
},
|
||||
});
|
||||
|
||||
const statusField = within(container).getByLabelText(
|
||||
"Search by user status",
|
||||
{
|
||||
exact: false,
|
||||
}
|
||||
);
|
||||
const bannedOption = within(statusField).getByText("Banned");
|
||||
|
||||
TestRenderer.act(() => {
|
||||
statusField.props.onChange({
|
||||
target: { value: bannedOption.props.value.toString() },
|
||||
});
|
||||
// TODO: Fix act warnings until await Promise.resolve();
|
||||
// or whatever comes out at https://github.com/facebook/react/issues/14769
|
||||
});
|
||||
|
||||
await waitForElement(() =>
|
||||
within(container).getByText("We could not find anyone", { exact: false })
|
||||
);
|
||||
});
|
||||
|
||||
it("can't change staff, moderator and admin status", async () => {
|
||||
const { container } = await createTestRenderer();
|
||||
["Admin", "Moderator", "Staff"].forEach(role => {
|
||||
const viewerRow = within(container).getByText(role, {
|
||||
selector: "tr",
|
||||
});
|
||||
expect(() =>
|
||||
within(viewerRow).getByLabelText("Change user status")
|
||||
).toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
it("ban user", async () => {
|
||||
const user = users.commenters[0];
|
||||
const banUser = createMutationResolverStub<typeof BanUserMutation>(
|
||||
variables => {
|
||||
expectAndFail(variables).toMatchObject({
|
||||
userID: user.id,
|
||||
});
|
||||
const userRecord = merge({}, user, {
|
||||
status: {
|
||||
current: user.status.current.concat(GQLUSER_STATUS.BANNED),
|
||||
banned: { active: true },
|
||||
},
|
||||
});
|
||||
return {
|
||||
user: userRecord,
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
const { container, testRenderer } = await createTestRenderer({
|
||||
Mutation: { banUser },
|
||||
});
|
||||
|
||||
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")
|
||||
.props.onClick();
|
||||
});
|
||||
|
||||
const modal = within(testRenderer.root).getByLabelText(
|
||||
"Are you sure you want to ban",
|
||||
{
|
||||
exact: false,
|
||||
}
|
||||
);
|
||||
|
||||
expect(within(modal).toJSON()).toMatchSnapshot();
|
||||
|
||||
within(modal)
|
||||
.getByText("Ban User")
|
||||
.props.onClick();
|
||||
within(userRow).getByText("Banned");
|
||||
expect(banUser.called).toBe(true);
|
||||
});
|
||||
|
||||
it("remove user ban", async () => {
|
||||
const user = users.bannedCommenter;
|
||||
const removeUserBan = createMutationResolverStub<
|
||||
typeof RemoveUserBanMutation
|
||||
>(variables => {
|
||||
expectAndFail(variables).toMatchObject({
|
||||
userID: user.id,
|
||||
});
|
||||
const userRecord = merge({}, user, {
|
||||
status: {
|
||||
current: user.status.current.filter(s => s !== GQLUSER_STATUS.BANNED),
|
||||
banned: { active: false },
|
||||
},
|
||||
});
|
||||
return {
|
||||
user: userRecord,
|
||||
};
|
||||
});
|
||||
|
||||
const { container } = await createTestRenderer({
|
||||
Mutation: { removeUserBan },
|
||||
Query: {
|
||||
users: createQueryResolverStub<QueryToUsersResolver>(() => ({
|
||||
edges: [
|
||||
{
|
||||
node: user,
|
||||
cursor: user.createdAt,
|
||||
},
|
||||
],
|
||||
pageInfo: { endCursor: null, hasNextPage: false },
|
||||
})),
|
||||
},
|
||||
});
|
||||
|
||||
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 Ban")
|
||||
.props.onClick();
|
||||
});
|
||||
|
||||
within(userRow).getByText("Active");
|
||||
expect(removeUserBan.called).toBe(true);
|
||||
});
|
||||
|
||||
@@ -24,7 +24,7 @@ const createTestRenderer = async (resolver: any = {}) => {
|
||||
settings: sinon
|
||||
.stub()
|
||||
.returns(merge({}, settings, get(resolver, "Query.settings"))),
|
||||
viewer: sinon.stub().returns(users[0]),
|
||||
viewer: sinon.stub().returns(users.admins[0]),
|
||||
},
|
||||
};
|
||||
const { testRenderer } = create({
|
||||
|
||||
@@ -28,7 +28,7 @@ const createTestRenderer = async (resolver: any = {}) => {
|
||||
.returns(
|
||||
merge({}, settingsWithEmptyAuth, get(resolver, "Query.settings"))
|
||||
),
|
||||
viewer: sinon.stub().returns(users[0]),
|
||||
viewer: sinon.stub().returns(users.admins[0]),
|
||||
},
|
||||
};
|
||||
const { testRenderer } = create({
|
||||
|
||||
@@ -29,7 +29,7 @@ const createTestRenderer = async (
|
||||
settings: sinon
|
||||
.stub()
|
||||
.returns(merge({}, settings, get(resolver, "Query.settings"))),
|
||||
viewer: sinon.stub().returns(users[0]),
|
||||
viewer: sinon.stub().returns(users.admins[0]),
|
||||
},
|
||||
};
|
||||
const { testRenderer } = create({
|
||||
|
||||
@@ -24,7 +24,7 @@ const createTestRenderer = async (resolver: any = {}) => {
|
||||
settings: sinon
|
||||
.stub()
|
||||
.returns(merge({}, settings, get(resolver, "Query.settings"))),
|
||||
viewer: sinon.stub().returns(users[0]),
|
||||
viewer: sinon.stub().returns(users.admins[0]),
|
||||
},
|
||||
};
|
||||
const { testRenderer } = create({
|
||||
|
||||
@@ -24,7 +24,7 @@ const createTestRenderer = async (resolver: any = {}) => {
|
||||
settings: sinon
|
||||
.stub()
|
||||
.returns(merge({}, settings, get(resolver, "Query.settings"))),
|
||||
viewer: sinon.stub().returns(users[0]),
|
||||
viewer: sinon.stub().returns(users.admins[0]),
|
||||
},
|
||||
};
|
||||
const { testRenderer } = create({
|
||||
|
||||
@@ -47,7 +47,7 @@ it("denies access to moderators", async () => {
|
||||
for (const r of deniedRoles) {
|
||||
const { testRenderer } = await createTestRenderer({
|
||||
Query: {
|
||||
viewer: sinon.stub().returns({ ...users[0], role: r }),
|
||||
viewer: sinon.stub().returns({ ...users.admins[0], role: r }),
|
||||
},
|
||||
});
|
||||
await waitForElement(() =>
|
||||
@@ -61,7 +61,7 @@ it("allows access to admins", async () => {
|
||||
for (const r of deniedRoles) {
|
||||
const { testRenderer } = await createTestRenderer({
|
||||
Query: {
|
||||
viewer: sinon.stub().returns({ ...users[0], role: r }),
|
||||
viewer: sinon.stub().returns({ ...users.admins[0], role: r }),
|
||||
},
|
||||
});
|
||||
await waitForElement(() =>
|
||||
|
||||
@@ -24,7 +24,7 @@ const createTestRenderer = async (resolver: any = {}) => {
|
||||
settings: sinon
|
||||
.stub()
|
||||
.returns(merge({}, settings, get(resolver, "Query.settings"))),
|
||||
viewer: sinon.stub().returns(users[0]),
|
||||
viewer: sinon.stub().returns(users.admins[0]),
|
||||
},
|
||||
};
|
||||
const { testRenderer } = create({
|
||||
|
||||
@@ -62,7 +62,7 @@ const createTestRenderer = async (resolver: any = {}) => {
|
||||
Query: {
|
||||
...resolver.Query,
|
||||
viewer: sinon.stub().returns({
|
||||
...users[0],
|
||||
...users.admins[0],
|
||||
commentModerationActionHistory,
|
||||
}),
|
||||
settings: sinon
|
||||
|
||||
@@ -1,10 +1,24 @@
|
||||
import { merge } from "lodash";
|
||||
import {
|
||||
GQLComment,
|
||||
GQLCOMMENT_STATUS,
|
||||
GQLCommentModerationAction,
|
||||
GQLCommentsConnection,
|
||||
GQLMODERATION_MODE,
|
||||
GQLModerationQueues,
|
||||
GQLSettings,
|
||||
GQLStoriesConnection,
|
||||
GQLStory,
|
||||
GQLSTORY_STATUS,
|
||||
GQLUser,
|
||||
GQLUSER_ROLE,
|
||||
GQLUSER_STATUS,
|
||||
GQLUsersConnection,
|
||||
} from "talk-framework/schema";
|
||||
import { createFixture, createFixtures } from "talk-framework/testHelpers";
|
||||
|
||||
import { GQLStory, GQLSTORY_STATUS, GQLUSER_ROLE } from "talk-framework/schema";
|
||||
|
||||
export const settings = {
|
||||
export const settings = createFixture<GQLSettings>({
|
||||
id: "settings",
|
||||
moderation: "POST",
|
||||
moderation: GQLMODERATION_MODE.POST,
|
||||
premodLinksEnable: false,
|
||||
wordList: {
|
||||
suspect: ["idiot", "stupid"],
|
||||
@@ -24,7 +38,7 @@ export const settings = {
|
||||
timeout: 604800,
|
||||
message: "Comments are closed on this story.",
|
||||
},
|
||||
customCSSURL: null,
|
||||
customCSSURL: "",
|
||||
domains: ["localhost:8080"],
|
||||
editCommentWindowLength: 30000,
|
||||
communityGuidelines: {
|
||||
@@ -61,7 +75,7 @@ export const settings = {
|
||||
admin: true,
|
||||
stream: true,
|
||||
},
|
||||
key: null,
|
||||
key: "",
|
||||
keyGeneratedAt: null,
|
||||
},
|
||||
google: {
|
||||
@@ -101,71 +115,73 @@ export const settings = {
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
export const settingsWithEmptyAuth = {
|
||||
...settings,
|
||||
id: "settings",
|
||||
auth: {
|
||||
integrations: {
|
||||
local: {
|
||||
enabled: true,
|
||||
allowRegistration: true,
|
||||
targetFilter: {
|
||||
admin: true,
|
||||
stream: true,
|
||||
export const settingsWithEmptyAuth = createFixture<GQLSettings>(
|
||||
{
|
||||
id: "settings",
|
||||
auth: {
|
||||
integrations: {
|
||||
local: {
|
||||
enabled: true,
|
||||
allowRegistration: true,
|
||||
targetFilter: {
|
||||
admin: true,
|
||||
stream: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
sso: {
|
||||
enabled: false,
|
||||
allowRegistration: true,
|
||||
targetFilter: {
|
||||
admin: true,
|
||||
stream: true,
|
||||
sso: {
|
||||
enabled: false,
|
||||
allowRegistration: true,
|
||||
targetFilter: {
|
||||
admin: true,
|
||||
stream: true,
|
||||
},
|
||||
key: "",
|
||||
keyGeneratedAt: null,
|
||||
},
|
||||
key: null,
|
||||
keyGeneratedAt: null,
|
||||
},
|
||||
google: {
|
||||
enabled: false,
|
||||
allowRegistration: true,
|
||||
targetFilter: {
|
||||
admin: true,
|
||||
stream: true,
|
||||
google: {
|
||||
enabled: false,
|
||||
allowRegistration: true,
|
||||
targetFilter: {
|
||||
admin: true,
|
||||
stream: true,
|
||||
},
|
||||
clientID: "",
|
||||
clientSecret: "",
|
||||
callbackURL: "http://localhost/google/callback",
|
||||
redirectURL: "http://localhost/google",
|
||||
},
|
||||
clientID: "",
|
||||
clientSecret: "",
|
||||
callbackURL: "http://localhost/google/callback",
|
||||
redirectURL: "http://localhost/google",
|
||||
},
|
||||
facebook: {
|
||||
enabled: false,
|
||||
allowRegistration: true,
|
||||
targetFilter: {
|
||||
admin: true,
|
||||
stream: true,
|
||||
facebook: {
|
||||
enabled: false,
|
||||
allowRegistration: true,
|
||||
targetFilter: {
|
||||
admin: true,
|
||||
stream: true,
|
||||
},
|
||||
clientID: "",
|
||||
clientSecret: "",
|
||||
callbackURL: "http://localhost/facebook/callback",
|
||||
redirectURL: "http://localhost/facebook",
|
||||
},
|
||||
clientID: "",
|
||||
clientSecret: "",
|
||||
callbackURL: "http://localhost/facebook/callback",
|
||||
redirectURL: "http://localhost/facebook",
|
||||
},
|
||||
oidc: {
|
||||
enabled: false,
|
||||
allowRegistration: false,
|
||||
targetFilter: {
|
||||
admin: true,
|
||||
stream: true,
|
||||
oidc: {
|
||||
enabled: false,
|
||||
allowRegistration: false,
|
||||
targetFilter: {
|
||||
admin: true,
|
||||
stream: true,
|
||||
},
|
||||
name: "",
|
||||
callbackURL: "http://localhost/oidc/callback",
|
||||
redirectURL: "http://localhost/oidc",
|
||||
},
|
||||
name: "",
|
||||
callbackURL: "http://localhost/oidc/callback",
|
||||
redirectURL: "http://localhost/oidc",
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
settings
|
||||
);
|
||||
|
||||
export const moderationActions = [
|
||||
export const moderationActions = createFixtures<GQLCommentModerationAction>([
|
||||
{
|
||||
id: "07e8f815-e165-4b5d-b438-7163415c8cf7",
|
||||
revision: {
|
||||
@@ -179,7 +195,7 @@ export const moderationActions = [
|
||||
},
|
||||
},
|
||||
createdAt: "2018-11-29T16:01:51.897Z",
|
||||
status: "ACCEPTED",
|
||||
status: GQLCOMMENT_STATUS.ACCEPTED,
|
||||
__typename: "CommentModerationAction",
|
||||
},
|
||||
{
|
||||
@@ -195,7 +211,7 @@ export const moderationActions = [
|
||||
},
|
||||
},
|
||||
createdAt: "2018-11-29T16:01:45.644Z",
|
||||
status: "REJECTED",
|
||||
status: GQLCOMMENT_STATUS.REJECTED,
|
||||
__typename: "CommentModerationAction",
|
||||
},
|
||||
{
|
||||
@@ -211,7 +227,7 @@ export const moderationActions = [
|
||||
},
|
||||
},
|
||||
createdAt: "2018-11-29T16:01:42.060Z",
|
||||
status: "ACCEPTED",
|
||||
status: GQLCOMMENT_STATUS.ACCEPTED,
|
||||
__typename: "CommentModerationAction",
|
||||
},
|
||||
{
|
||||
@@ -227,7 +243,7 @@ export const moderationActions = [
|
||||
},
|
||||
},
|
||||
createdAt: "2018-11-29T16:01:34.539Z",
|
||||
status: "REJECTED",
|
||||
status: GQLCOMMENT_STATUS.REJECTED,
|
||||
__typename: "CommentModerationAction",
|
||||
},
|
||||
{
|
||||
@@ -243,45 +259,105 @@ export const moderationActions = [
|
||||
},
|
||||
},
|
||||
createdAt: "2018-11-29T16:01:30.648Z",
|
||||
status: "ACCEPTED",
|
||||
status: GQLCOMMENT_STATUS.ACCEPTED,
|
||||
__typename: "CommentModerationAction",
|
||||
},
|
||||
];
|
||||
]);
|
||||
|
||||
export const baseUser = {
|
||||
export const baseUser = createFixture<GQLUser>({
|
||||
profiles: [{ __typename: "LocalProfile" }],
|
||||
createdAt: "2018-07-06T18:24:00.000Z",
|
||||
status: {
|
||||
current: [],
|
||||
ban: {
|
||||
active: false,
|
||||
history: [],
|
||||
},
|
||||
suspension: {
|
||||
active: false,
|
||||
history: [],
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export const users = {
|
||||
admins: createFixtures<GQLUser>(
|
||||
[
|
||||
{
|
||||
id: "user-admin-0",
|
||||
username: "Markus",
|
||||
email: "markus@test.com",
|
||||
role: GQLUSER_ROLE.ADMIN,
|
||||
},
|
||||
],
|
||||
baseUser
|
||||
),
|
||||
moderators: createFixtures<GQLUser>(
|
||||
[
|
||||
{
|
||||
id: "user-moderator-0",
|
||||
username: "Lukas",
|
||||
email: "lukas@test.com",
|
||||
role: GQLUSER_ROLE.MODERATOR,
|
||||
},
|
||||
],
|
||||
baseUser
|
||||
),
|
||||
staff: createFixtures<GQLUser>(
|
||||
[
|
||||
{
|
||||
id: "user-staff-0",
|
||||
username: "Huy",
|
||||
email: "huy@test.com",
|
||||
role: GQLUSER_ROLE.STAFF,
|
||||
},
|
||||
],
|
||||
baseUser
|
||||
),
|
||||
commenters: createFixtures<GQLUser>(
|
||||
[
|
||||
{
|
||||
id: "user-commenter-0",
|
||||
username: "Isabelle",
|
||||
email: "isabelle@test.com",
|
||||
role: GQLUSER_ROLE.COMMENTER,
|
||||
},
|
||||
{
|
||||
id: "user-commenter-1",
|
||||
username: "Ngoc",
|
||||
email: "ngoc@test.com",
|
||||
role: GQLUSER_ROLE.COMMENTER,
|
||||
},
|
||||
{
|
||||
id: "user-commenter-2",
|
||||
username: "Max",
|
||||
email: "max@test.com",
|
||||
role: GQLUSER_ROLE.COMMENTER,
|
||||
},
|
||||
],
|
||||
baseUser
|
||||
),
|
||||
bannedCommenter: createFixture<GQLUser>(
|
||||
{
|
||||
id: "user-banned-0",
|
||||
username: "Ingrid",
|
||||
email: "ingrid@test.com",
|
||||
role: GQLUSER_ROLE.COMMENTER,
|
||||
status: {
|
||||
current: [GQLUSER_STATUS.BANNED],
|
||||
ban: { active: true },
|
||||
suspension: { active: true },
|
||||
},
|
||||
},
|
||||
baseUser
|
||||
),
|
||||
};
|
||||
|
||||
export const users = [
|
||||
{
|
||||
...baseUser,
|
||||
id: "user-0",
|
||||
username: "Markus",
|
||||
email: "markus@test.com",
|
||||
role: GQLUSER_ROLE.ADMIN,
|
||||
},
|
||||
{
|
||||
...baseUser,
|
||||
id: "user-1",
|
||||
username: "Lukas",
|
||||
email: "lukas@test.com",
|
||||
role: GQLUSER_ROLE.MODERATOR,
|
||||
},
|
||||
{
|
||||
...baseUser,
|
||||
id: "user-2",
|
||||
username: "Isabelle",
|
||||
email: "isabelle@test.com",
|
||||
role: GQLUSER_ROLE.COMMENTER,
|
||||
},
|
||||
];
|
||||
|
||||
export const baseComment = {
|
||||
author: users[0],
|
||||
export const baseComment = createFixture<GQLComment>({
|
||||
author: users.commenters[0],
|
||||
body: "Comment Body",
|
||||
createdAt: "2018-07-06T18:24:00.000Z",
|
||||
status: "NONE",
|
||||
status: GQLCOMMENT_STATUS.NONE,
|
||||
actionCounts: {
|
||||
flag: {
|
||||
reasons: {
|
||||
@@ -296,68 +372,71 @@ export const baseComment = {
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
export const reportedComments = [
|
||||
merge({}, baseComment, {
|
||||
id: "comment-0",
|
||||
author: users[0],
|
||||
revision: {
|
||||
id: "comment-0-revision-0",
|
||||
},
|
||||
permalink: "http://localhost/comment/0",
|
||||
body:
|
||||
"This is the last random sentence I will be writing and I am going to stop mid-sent",
|
||||
actionCounts: {
|
||||
flag: {
|
||||
reasons: {
|
||||
COMMENT_REPORTED_SPAM: 2,
|
||||
export const reportedComments = createFixtures<GQLComment>(
|
||||
[
|
||||
{
|
||||
id: "comment-0",
|
||||
author: users.commenters[0],
|
||||
revision: {
|
||||
id: "comment-0-revision-0",
|
||||
},
|
||||
permalink: "http://localhost/comment/0",
|
||||
body:
|
||||
"This is the last random sentence I will be writing and I am going to stop mid-sent",
|
||||
actionCounts: {
|
||||
flag: {
|
||||
reasons: {
|
||||
COMMENT_REPORTED_SPAM: 2,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
merge({}, baseComment, {
|
||||
id: "comment-1",
|
||||
revision: {
|
||||
id: "comment-1-revision-1",
|
||||
},
|
||||
permalink: "http://localhost/comment/1",
|
||||
author: users[1],
|
||||
body: "Don't fool with me",
|
||||
actionCounts: {
|
||||
flag: {
|
||||
reasons: {
|
||||
COMMENT_REPORTED_OFFENSIVE: 3,
|
||||
{
|
||||
id: "comment-1",
|
||||
revision: {
|
||||
id: "comment-1-revision-1",
|
||||
},
|
||||
permalink: "http://localhost/comment/1",
|
||||
author: users.commenters[1],
|
||||
body: "Don't fool with me",
|
||||
actionCounts: {
|
||||
flag: {
|
||||
reasons: {
|
||||
COMMENT_REPORTED_OFFENSIVE: 3,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
merge({}, baseComment, {
|
||||
id: "comment-2",
|
||||
revision: {
|
||||
id: "comment-2-revision-2",
|
||||
},
|
||||
permalink: "http://localhost/comment/2",
|
||||
status: "PREMOD",
|
||||
author: users[2],
|
||||
body: "I think I deserve better",
|
||||
actionCounts: {
|
||||
flag: {
|
||||
reasons: {
|
||||
COMMENT_REPORTED_SPAM: 1,
|
||||
COMMENT_REPORTED_OFFENSIVE: 1,
|
||||
{
|
||||
id: "comment-2",
|
||||
revision: {
|
||||
id: "comment-2-revision-2",
|
||||
},
|
||||
permalink: "http://localhost/comment/2",
|
||||
status: GQLCOMMENT_STATUS.PREMOD,
|
||||
author: users.commenters[2],
|
||||
body: "I think I deserve better",
|
||||
actionCounts: {
|
||||
flag: {
|
||||
reasons: {
|
||||
COMMENT_REPORTED_SPAM: 1,
|
||||
COMMENT_REPORTED_OFFENSIVE: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
];
|
||||
],
|
||||
baseComment
|
||||
);
|
||||
|
||||
export const rejectedComments = reportedComments.map(c => ({
|
||||
export const rejectedComments = reportedComments.map<GQLComment>(c => ({
|
||||
...c,
|
||||
status: "REJECTED",
|
||||
status: GQLCOMMENT_STATUS.REJECTED,
|
||||
}));
|
||||
|
||||
export const emptyModerationQueues = {
|
||||
export const emptyModerationQueues = createFixture<GQLModerationQueues>({
|
||||
reported: {
|
||||
id: "reported",
|
||||
count: 0,
|
||||
@@ -373,27 +452,29 @@ export const emptyModerationQueues = {
|
||||
count: 0,
|
||||
comments: { edges: [], pageInfo: { endCursor: null, hasNextPage: false } },
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
export const emptyRejectedComments = {
|
||||
export const emptyRejectedComments = createFixture<GQLCommentsConnection>({
|
||||
edges: [],
|
||||
pageInfo: { endCursor: null, hasNextPage: false },
|
||||
};
|
||||
});
|
||||
|
||||
export const communityUsers = {
|
||||
export const communityUsers = createFixture<GQLUsersConnection>({
|
||||
edges: [
|
||||
{ node: users[0], cursor: users[0].createdAt },
|
||||
{ node: users[1], cursor: users[1].createdAt },
|
||||
{ node: users.admins[0], cursor: users.admins[0].createdAt },
|
||||
{ node: users.moderators[0], cursor: users.moderators[0].createdAt },
|
||||
{ node: users.staff[0], cursor: users.staff[0].createdAt },
|
||||
{ node: users.commenters[0], cursor: users.commenters[0].createdAt },
|
||||
],
|
||||
pageInfo: { endCursor: null, hasNextPage: false },
|
||||
};
|
||||
});
|
||||
|
||||
export const emptyCommunityUsers = {
|
||||
export const emptyCommunityUsers = createFixture<GQLUsersConnection>({
|
||||
edges: [],
|
||||
pageInfo: { endCursor: null, hasNextPage: false },
|
||||
};
|
||||
});
|
||||
|
||||
export const stories: Array<Partial<GQLStory>> = [
|
||||
export const stories = createFixtures<GQLStory>([
|
||||
{
|
||||
id: "story-1",
|
||||
closedAt: null,
|
||||
@@ -430,17 +511,17 @@ export const stories: Array<Partial<GQLStory>> = [
|
||||
publishedAt: "2018-11-29T16:01:51.897Z",
|
||||
},
|
||||
},
|
||||
];
|
||||
]);
|
||||
|
||||
export const storyConnection = {
|
||||
export const storyConnection = createFixture<GQLStoriesConnection>({
|
||||
edges: [
|
||||
{ node: stories[0], cursor: stories[0].createdAt },
|
||||
{ node: stories[1], cursor: stories[1].createdAt },
|
||||
],
|
||||
pageInfo: { endCursor: null, hasNextPage: false },
|
||||
};
|
||||
});
|
||||
|
||||
export const emptyStories = {
|
||||
export const emptyStories = createFixture<GQLStoriesConnection>({
|
||||
edges: [],
|
||||
pageInfo: { endCursor: null, hasNextPage: false },
|
||||
};
|
||||
});
|
||||
|
||||
@@ -157,7 +157,7 @@ exports[`rejected queue accepts comment in rejected queue: dangling 1`] = `
|
||||
<span
|
||||
className="Typography-root Typography-heading2 Typography-colorTextPrimary ModerateCard-username Username-root"
|
||||
>
|
||||
Markus
|
||||
Isabelle
|
||||
</span>
|
||||
<time
|
||||
className="Timestamp-root RelativeTime-root"
|
||||
@@ -310,7 +310,7 @@ exports[`rejected queue renders rejected queue with comments 1`] = `
|
||||
<span
|
||||
className="Typography-root Typography-heading2 Typography-colorTextPrimary ModerateCard-username Username-root"
|
||||
>
|
||||
Markus
|
||||
Isabelle
|
||||
</span>
|
||||
<time
|
||||
className="Timestamp-root RelativeTime-root"
|
||||
@@ -450,7 +450,7 @@ exports[`rejected queue renders rejected queue with comments 1`] = `
|
||||
<span
|
||||
className="Typography-root Typography-heading2 Typography-colorTextPrimary ModerateCard-username Username-root"
|
||||
>
|
||||
Lukas
|
||||
Ngoc
|
||||
</span>
|
||||
<time
|
||||
className="Timestamp-root RelativeTime-root"
|
||||
@@ -597,7 +597,7 @@ exports[`rejected queue renders rejected queue with comments and load more 1`] =
|
||||
<span
|
||||
className="Typography-root Typography-heading2 Typography-colorTextPrimary ModerateCard-username Username-root"
|
||||
>
|
||||
Isabelle
|
||||
Max
|
||||
</span>
|
||||
<time
|
||||
className="Timestamp-root RelativeTime-root"
|
||||
@@ -766,7 +766,7 @@ exports[`reported queue accepts comment in reported queue: dangling 1`] = `
|
||||
<span
|
||||
className="Typography-root Typography-heading2 Typography-colorTextPrimary ModerateCard-username Username-root"
|
||||
>
|
||||
Markus
|
||||
Isabelle
|
||||
</span>
|
||||
<time
|
||||
className="Timestamp-root RelativeTime-root"
|
||||
@@ -922,7 +922,7 @@ exports[`reported queue rejects comment in reported queue: dangling 1`] = `
|
||||
<span
|
||||
className="Typography-root Typography-heading2 Typography-colorTextPrimary ModerateCard-username Username-root"
|
||||
>
|
||||
Markus
|
||||
Isabelle
|
||||
</span>
|
||||
<time
|
||||
className="Timestamp-root RelativeTime-root"
|
||||
@@ -1090,7 +1090,7 @@ exports[`reported queue renders reported queue with comments 1`] = `
|
||||
<span
|
||||
className="Typography-root Typography-heading2 Typography-colorTextPrimary ModerateCard-username Username-root"
|
||||
>
|
||||
Markus
|
||||
Isabelle
|
||||
</span>
|
||||
<time
|
||||
className="Timestamp-root RelativeTime-root"
|
||||
@@ -1230,7 +1230,7 @@ exports[`reported queue renders reported queue with comments 1`] = `
|
||||
<span
|
||||
className="Typography-root Typography-heading2 Typography-colorTextPrimary ModerateCard-username Username-root"
|
||||
>
|
||||
Lukas
|
||||
Ngoc
|
||||
</span>
|
||||
<time
|
||||
className="Timestamp-root RelativeTime-root"
|
||||
@@ -1376,7 +1376,7 @@ exports[`reported queue renders reported queue with comments and load more 1`] =
|
||||
<span
|
||||
className="Typography-root Typography-heading2 Typography-colorTextPrimary ModerateCard-username Username-root"
|
||||
>
|
||||
Isabelle
|
||||
Max
|
||||
</span>
|
||||
<time
|
||||
className="Timestamp-root RelativeTime-root"
|
||||
@@ -1537,7 +1537,7 @@ exports[`single comment view accepts single comment 1`] = `
|
||||
<span
|
||||
className="Typography-root Typography-heading2 Typography-colorTextPrimary ModerateCard-username Username-root"
|
||||
>
|
||||
Markus
|
||||
Isabelle
|
||||
</span>
|
||||
<time
|
||||
className="Timestamp-root RelativeTime-root"
|
||||
@@ -1680,7 +1680,7 @@ exports[`single comment view rejects single comment 1`] = `
|
||||
<span
|
||||
className="Typography-root Typography-heading2 Typography-colorTextPrimary ModerateCard-username Username-root"
|
||||
>
|
||||
Markus
|
||||
Isabelle
|
||||
</span>
|
||||
<time
|
||||
className="Timestamp-root RelativeTime-root"
|
||||
@@ -1858,7 +1858,7 @@ exports[`single comment view renders single comment view 1`] = `
|
||||
<span
|
||||
className="Typography-root Typography-heading2 Typography-colorTextPrimary ModerateCard-username Username-root"
|
||||
>
|
||||
Markus
|
||||
Isabelle
|
||||
</span>
|
||||
<time
|
||||
className="Timestamp-root RelativeTime-root"
|
||||
|
||||
@@ -44,7 +44,7 @@ const createTestRenderer = async (resolver: any = {}) => {
|
||||
comments:
|
||||
get(resolver, "Query.comments") ||
|
||||
sinon.stub().returns(emptyRejectedComments),
|
||||
viewer: sinon.stub().returns(users[0]),
|
||||
viewer: sinon.stub().returns(users.admins[0]),
|
||||
},
|
||||
};
|
||||
const { testRenderer } = create({
|
||||
|
||||
@@ -36,7 +36,7 @@ const createTestRenderer = async (resolver: any = {}) => {
|
||||
expectAndFail(data.status).toBeFalsy();
|
||||
return storyConnection;
|
||||
}),
|
||||
viewer: sinon.stub().returns(users[0]),
|
||||
viewer: sinon.stub().returns(users.admins[0]),
|
||||
...resolver.Query,
|
||||
},
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user