mirror of
https://github.com/wassname/talk.git
synced 2026-07-04 01:25:19 +08:00
[next] Bugfixes (#2272)
* feat: suspending, banning, now propogation * feat: new mutation api with hooks support * [CORL-343] Center Spinner in Stream * [CORL-344] Fix moderation card styling * [CORL-338] Fix permalink reply bug * [CORL-337] Fix community guidelines box width * [CORL-341] Toggle reply form view when clicking on reply * test: add tests * [CORL-333] Fix bug: removing message box icon; [CORL-336] Fix bug: allow resetting custom css
This commit is contained in:
@@ -46,7 +46,7 @@ const BanUserMutation = createMutation(
|
||||
current: lookup<GQLUser>(
|
||||
environment,
|
||||
input.userID
|
||||
)!.status!.current!.concat([GQLUSER_STATUS.BANNED]),
|
||||
)!.status.current.concat([GQLUSER_STATUS.BANNED]),
|
||||
ban: {
|
||||
active: true,
|
||||
},
|
||||
|
||||
@@ -46,7 +46,7 @@ const RemoveUserBanMutation = createMutation(
|
||||
current: lookup<GQLUser>(
|
||||
environment,
|
||||
input.userID
|
||||
)!.status!.current!.filter(s => s !== GQLUSER_STATUS.BANNED),
|
||||
)!.status.current.filter(s => s !== GQLUSER_STATUS.BANNED),
|
||||
ban: {
|
||||
active: false,
|
||||
},
|
||||
|
||||
+2
-1
@@ -2,6 +2,7 @@ import { Localized } from "fluent-react/compat";
|
||||
import React, { StatelessComponent } from "react";
|
||||
import { Field } from "react-final-form";
|
||||
|
||||
import { formatEmpty, parseEmptyAsNull } from "talk-framework/lib/form";
|
||||
import {
|
||||
FormField,
|
||||
HorizontalGutter,
|
||||
@@ -33,7 +34,7 @@ const CustomCSSConfig: StatelessComponent<Props> = ({ disabled }) => (
|
||||
styles. Can be internal or external.
|
||||
</Typography>
|
||||
</Localized>
|
||||
<Field name="customCSSURL">
|
||||
<Field name="customCSSURL" parse={parseEmptyAsNull} format={formatEmpty}>
|
||||
{({ input, meta }) => (
|
||||
<>
|
||||
<TextField
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
.root {
|
||||
composes: root from "talk-stream/shared/htmlContent.css";
|
||||
|
||||
mark {
|
||||
background-color: var(--palette-highlight);
|
||||
padding: 0 2px;
|
||||
|
||||
@@ -26,6 +26,8 @@ import {
|
||||
users,
|
||||
} from "../fixtures";
|
||||
|
||||
const viewer = users.admins[0];
|
||||
|
||||
beforeEach(async () => {
|
||||
replaceHistoryLocation("http://localhost/admin/community");
|
||||
});
|
||||
@@ -43,7 +45,7 @@ const createTestRenderer = async (
|
||||
expectAndFail(variables.role).toBeFalsy();
|
||||
return communityUsers;
|
||||
},
|
||||
viewer: () => users.admins[0],
|
||||
viewer: () => viewer,
|
||||
},
|
||||
}),
|
||||
params.resolvers
|
||||
@@ -113,7 +115,6 @@ it("filter by role", async () => {
|
||||
});
|
||||
|
||||
it("can't change viewer role", async () => {
|
||||
const viewer = users.admins[0];
|
||||
const { container } = await createTestRenderer();
|
||||
|
||||
const viewerRow = within(container).getByText(viewer.username!, {
|
||||
@@ -169,11 +170,11 @@ it("change user role", async () => {
|
||||
});
|
||||
|
||||
it("can't change role as a moderator", async () => {
|
||||
const viewer = users.moderators[0];
|
||||
const moderator = users.moderators[0];
|
||||
const { container } = await createTestRenderer({
|
||||
resolvers: createResolversStub<GQLResolver>({
|
||||
Query: {
|
||||
viewer: () => viewer,
|
||||
viewer: () => moderator,
|
||||
},
|
||||
}),
|
||||
});
|
||||
@@ -190,8 +191,8 @@ it("load more", async () => {
|
||||
return {
|
||||
edges: [
|
||||
{
|
||||
node: users.admins[0],
|
||||
cursor: users.admins[0].createdAt,
|
||||
node: viewer,
|
||||
cursor: viewer.createdAt,
|
||||
},
|
||||
{
|
||||
node: users.commenters[0],
|
||||
|
||||
@@ -105,6 +105,43 @@ it("change custom css", async () => {
|
||||
expect(resolvers.Mutation!.updateSettings!.called).toBe(true);
|
||||
});
|
||||
|
||||
it("remove custom css", async () => {
|
||||
const resolvers = createResolversStub<GQLResolver>({
|
||||
Query: {
|
||||
settings: () =>
|
||||
pureMerge<typeof settings>(settings, {
|
||||
customCSSURL: "./custom.css",
|
||||
}),
|
||||
},
|
||||
Mutation: {
|
||||
updateSettings: ({ variables }) => {
|
||||
expectAndFail(variables.settings.customCSSURL).toBeNull();
|
||||
return {
|
||||
settings: pureMerge(settings, variables.settings),
|
||||
};
|
||||
},
|
||||
},
|
||||
});
|
||||
const { configureContainer, advancedContainer } = await createTestRenderer({
|
||||
resolvers,
|
||||
});
|
||||
|
||||
const customCSSField = within(advancedContainer).getByLabelText("Custom CSS");
|
||||
|
||||
// Let's change the customCSS field.
|
||||
customCSSField.props.onChange("");
|
||||
|
||||
// Send form
|
||||
within(configureContainer)
|
||||
.getByType("form")
|
||||
.props.onSubmit();
|
||||
|
||||
// Wait for submission to be finished
|
||||
await wait(() => {
|
||||
expect(resolvers.Mutation!.updateSettings!.called).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
it("change permitted domains to be empty", async () => {
|
||||
const resolvers = createResolversStub<GQLResolver>({
|
||||
Mutation: {
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
import { Environment } from "relay-runtime";
|
||||
|
||||
export default function getStory(environment: Environment, id: string) {
|
||||
return environment
|
||||
.getStore()
|
||||
.getSource()
|
||||
.get(id);
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
import { Environment } from "relay-runtime";
|
||||
|
||||
import getStory from "./getStory";
|
||||
|
||||
export default function getStorySettings(environment: Environment, id: string) {
|
||||
const story = getStory(environment, id);
|
||||
if (!story) {
|
||||
return null;
|
||||
}
|
||||
const storySettingsRef = story.settings.__ref;
|
||||
return environment
|
||||
.getStore()
|
||||
.getSource()
|
||||
.get(storySettingsRef);
|
||||
}
|
||||
@@ -1,12 +1,13 @@
|
||||
import { Environment } from "relay-runtime";
|
||||
import { lookup } from "talk-framework/lib/relay";
|
||||
import { GQLUser } from "talk-framework/schema";
|
||||
|
||||
import getViewerSourceID from "./getViewerSourceID";
|
||||
|
||||
export default function getViewer(environment: Environment) {
|
||||
const source = environment.getStore().getSource();
|
||||
const viewerID = getViewerSourceID(environment);
|
||||
if (!viewerID) {
|
||||
return null;
|
||||
}
|
||||
return source.get(viewerID)!;
|
||||
return lookup<GQLUser>(environment, viewerID)!;
|
||||
}
|
||||
|
||||
@@ -7,5 +7,3 @@ export { default as redirectOAuth2 } from "./redirectOAuth2";
|
||||
export {
|
||||
default as getParamsFromHashAndClearIt,
|
||||
} from "./getParamsFromHashAndClearIt";
|
||||
export { default as getStory } from "./getStory";
|
||||
export { default as getStorySettings } from "./getStorySettings";
|
||||
|
||||
@@ -6,7 +6,7 @@ import { Environment, RelayInMemoryRecordSource } from "relay-runtime";
|
||||
*/
|
||||
type RecordSourceProxy<T> = T extends object
|
||||
? {
|
||||
readonly [P in keyof T]?: T[P] extends Array<infer U>
|
||||
readonly [P in keyof T]: T[P] extends Array<infer U>
|
||||
? ReadonlyArray<RecordSourceProxy<U>>
|
||||
: T[P] extends ReadonlyArray<infer V>
|
||||
? ReadonlyArray<RecordSourceProxy<V>>
|
||||
|
||||
@@ -1,33 +1,48 @@
|
||||
export function denormalizeComment(comment: any, parents: any[] = []) {
|
||||
const replyNodes =
|
||||
import { GQLComment, GQLCommentEdge, GQLStory } from "talk-framework/schema";
|
||||
|
||||
import createFixture, { Fixture } from "./createFixture";
|
||||
|
||||
export function denormalizeComment(
|
||||
comment: Fixture<GQLComment>,
|
||||
parents: Array<Fixture<GQLCommentEdge>> = []
|
||||
): GQLComment {
|
||||
const replyEdges =
|
||||
(comment.replies &&
|
||||
comment.replies.edges.map((edge: any) =>
|
||||
denormalizeComment(edge, [...parents, comment])
|
||||
)) ||
|
||||
comment.replies.edges &&
|
||||
comment.replies.edges.map(edge => ({
|
||||
...edge,
|
||||
node:
|
||||
edge.node &&
|
||||
denormalizeComment(edge.node, [
|
||||
...parents,
|
||||
{ node: comment, cursor: comment.createdAt },
|
||||
]),
|
||||
}))) ||
|
||||
[];
|
||||
const repliesPageInfo = (comment.replies && comment.replies.pageInfo) || {
|
||||
endCursor: null,
|
||||
hasNextPage: false,
|
||||
};
|
||||
return {
|
||||
return createFixture<GQLComment>({
|
||||
...comment,
|
||||
replies: { edges: replyNodes, pageInfo: repliesPageInfo },
|
||||
replyCount: replyNodes.length,
|
||||
replies: { edges: replyEdges, pageInfo: repliesPageInfo },
|
||||
replyCount: replyEdges.length,
|
||||
parentCount: parents.length,
|
||||
parents: {
|
||||
edges: parents,
|
||||
pageInfo: { startCursor: null, hasPreviousPage: false },
|
||||
},
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
export function denormalizeComments(commentList: any[]) {
|
||||
export function denormalizeComments(commentList: Array<Fixture<GQLComment>>) {
|
||||
return commentList.map(c => denormalizeComment(c));
|
||||
}
|
||||
|
||||
export function denormalizeStory(story: any) {
|
||||
export function denormalizeStory(story: Fixture<GQLStory>) {
|
||||
const commentNodes =
|
||||
(story.comments &&
|
||||
story.comments.edges &&
|
||||
story.comments.edges.map((edge: any) => ({
|
||||
...edge,
|
||||
node: denormalizeComment(edge.node),
|
||||
@@ -46,6 +61,6 @@ export function denormalizeStory(story: any) {
|
||||
};
|
||||
}
|
||||
|
||||
export function denormalizeStories(storyList: any[]) {
|
||||
export function denormalizeStories(storyList: Array<Fixture<GQLStory>>) {
|
||||
return storyList.map(a => denormalizeStory(a));
|
||||
}
|
||||
|
||||
@@ -6,15 +6,16 @@ import {
|
||||
RecordSourceSelectorProxy,
|
||||
} from "relay-runtime";
|
||||
|
||||
import { getStorySettings, getViewer } from "talk-framework/helpers";
|
||||
import { getViewer } from "talk-framework/helpers";
|
||||
import { TalkContext } from "talk-framework/lib/bootstrap";
|
||||
import {
|
||||
commitMutationPromiseNormalized,
|
||||
createMutationContainer,
|
||||
lookup,
|
||||
MutationInput,
|
||||
MutationResponsePromise,
|
||||
} from "talk-framework/lib/relay";
|
||||
import { GQLUSER_ROLE } from "talk-framework/schema";
|
||||
import { GQLStory, GQLUSER_ROLE } from "talk-framework/schema";
|
||||
import { CreateCommentMutation as MutationTypes } from "talk-stream/__generated__/CreateCommentMutation.graphql";
|
||||
|
||||
import {
|
||||
@@ -111,7 +112,8 @@ function commit(
|
||||
const currentDate = new Date().toISOString();
|
||||
const id = uuidGenerator();
|
||||
|
||||
const storySettings = getStorySettings(relayEnvironment, input.storyID);
|
||||
const storySettings = lookup<GQLStory>(relayEnvironment, input.storyID)!
|
||||
.settings;
|
||||
if (!storySettings || !storySettings.moderation) {
|
||||
throw new Error("Moderation mode of the story was not included");
|
||||
}
|
||||
|
||||
@@ -6,15 +6,16 @@ import {
|
||||
RecordSourceSelectorProxy,
|
||||
} from "relay-runtime";
|
||||
|
||||
import { getStorySettings, getViewer } from "talk-framework/helpers";
|
||||
import { getViewer } from "talk-framework/helpers";
|
||||
import { TalkContext } from "talk-framework/lib/bootstrap";
|
||||
import {
|
||||
commitMutationPromiseNormalized,
|
||||
createMutationContainer,
|
||||
lookup,
|
||||
MutationInput,
|
||||
MutationResponsePromise,
|
||||
} from "talk-framework/lib/relay";
|
||||
import { GQLUSER_ROLE } from "talk-framework/schema";
|
||||
import { GQLStory, GQLUSER_ROLE } from "talk-framework/schema";
|
||||
import { CreateCommentReplyMutation as MutationTypes } from "talk-stream/__generated__/CreateCommentReplyMutation.graphql";
|
||||
|
||||
import {
|
||||
@@ -137,8 +138,8 @@ function commit(
|
||||
const viewer = getViewer(environment)!;
|
||||
const currentDate = new Date().toISOString();
|
||||
const id = uuidGenerator();
|
||||
|
||||
const storySettings = getStorySettings(relayEnvironment, input.storyID);
|
||||
const storySettings = lookup<GQLStory>(relayEnvironment, input.storyID)!
|
||||
.settings;
|
||||
if (!storySettings || !storySettings.moderation) {
|
||||
throw new Error("Moderation mode of the story was not included");
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ interface Props {
|
||||
|
||||
const CommunityGuidelines: StatelessComponent<Props> = props => {
|
||||
return (
|
||||
<CallOut color="primary">
|
||||
<CallOut color="primary" fullWidth>
|
||||
<Markdown>{props.children}</Markdown>
|
||||
</CallOut>
|
||||
);
|
||||
|
||||
@@ -4,7 +4,7 @@ import { StatelessComponent } from "react";
|
||||
|
||||
import { PropTypesOf } from "talk-framework/types";
|
||||
import UserBoxContainer from "talk-stream/containers/UserBoxContainer";
|
||||
import { Button, HorizontalGutter, Spinner } from "talk-ui/components";
|
||||
import { Button, Flex, HorizontalGutter, Spinner } from "talk-ui/components";
|
||||
|
||||
import CommentContainer from "../containers/CommentContainer";
|
||||
import CommunityGuidelinesContainer from "../containers/CommunityGuidelinesContainer";
|
||||
@@ -52,7 +52,11 @@ const Stream: StatelessComponent<StreamProps> = props => {
|
||||
{props.comments.length > 0 && (
|
||||
<SortMenu orderBy={props.orderBy} onChange={props.onChangeOrderBy} />
|
||||
)}
|
||||
{props.refetching && <Spinner />}
|
||||
{props.refetching && (
|
||||
<Flex justifyContent="center">
|
||||
<Spinner />
|
||||
</Flex>
|
||||
)}
|
||||
{!props.refetching && (
|
||||
<HorizontalGutter
|
||||
id="talk-comments-stream-log"
|
||||
|
||||
+1
@@ -3,6 +3,7 @@
|
||||
exports[`renders correctly 1`] = `
|
||||
<withPropsOnChange(CallOut)
|
||||
color="primary"
|
||||
fullWidth={true}
|
||||
>
|
||||
<Markdown>
|
||||
**bold**
|
||||
|
||||
@@ -88,10 +88,10 @@ export class CommentContainer extends Component<Props, State> {
|
||||
);
|
||||
}
|
||||
|
||||
private openReplyDialog = () => {
|
||||
private toggleReplyDialog = () => {
|
||||
if (this.props.viewer) {
|
||||
this.setState(state => ({
|
||||
showReplyDialog: true,
|
||||
showReplyDialog: !state.showReplyDialog,
|
||||
}));
|
||||
} else {
|
||||
this.props.showAuthPopup({ view: "SIGN_IN" });
|
||||
@@ -206,7 +206,7 @@ export class CommentContainer extends Component<Props, State> {
|
||||
id={`comments-commentContainer-replyButton-${
|
||||
comment.id
|
||||
}`}
|
||||
onClick={this.openReplyDialog}
|
||||
onClick={this.toggleReplyDialog}
|
||||
active={showReplyDialog}
|
||||
disabled={
|
||||
settings.disableCommenting.enabled || story.isClosed
|
||||
|
||||
@@ -8,7 +8,7 @@ import {
|
||||
} from "talk-framework/lib/relay";
|
||||
import { StreamQuery as QueryTypes } from "talk-stream/__generated__/StreamQuery.graphql";
|
||||
import { StreamQueryLocal as Local } from "talk-stream/__generated__/StreamQueryLocal.graphql";
|
||||
import { Delay, Spinner } from "talk-ui/components";
|
||||
import { Delay, Flex, Spinner } from "talk-ui/components";
|
||||
import StreamContainer from "../containers/StreamContainer";
|
||||
|
||||
interface Props {
|
||||
@@ -43,7 +43,9 @@ export const render = (
|
||||
|
||||
return (
|
||||
<Delay>
|
||||
<Spinner />
|
||||
<Flex justifyContent="center">
|
||||
<Spinner />
|
||||
</Flex>
|
||||
</Delay>
|
||||
);
|
||||
};
|
||||
|
||||
+5
-1
@@ -10,7 +10,11 @@ exports[`renders loading 1`] = `
|
||||
<Delay
|
||||
ms={500}
|
||||
>
|
||||
<withPropsOnChange(Spinner) />
|
||||
<ForwardRef(forwardRef)
|
||||
justifyContent="center"
|
||||
>
|
||||
<withPropsOnChange(Spinner) />
|
||||
</ForwardRef(forwardRef)>
|
||||
</Delay>
|
||||
`;
|
||||
|
||||
|
||||
+4
@@ -68,6 +68,8 @@ const enhanced = withContext(ctx => ({
|
||||
fragment PermalinkViewContainer_story on Story {
|
||||
...ConversationThreadContainer_story
|
||||
...ReplyListContainer1_story
|
||||
...CreateCommentMutation_story
|
||||
...CreateCommentReplyMutation_story
|
||||
}
|
||||
`,
|
||||
comment: graphql`
|
||||
@@ -82,6 +84,8 @@ const enhanced = withContext(ctx => ({
|
||||
...ConversationThreadContainer_viewer
|
||||
...ReplyListContainer1_viewer
|
||||
...UserBoxContainer_viewer
|
||||
...CreateCommentMutation_viewer
|
||||
...CreateCommentReplyMutation_viewer
|
||||
}
|
||||
`,
|
||||
settings: graphql`
|
||||
|
||||
@@ -3,7 +3,11 @@ import React, { StatelessComponent, Suspense } from "react";
|
||||
import { Field } from "react-final-form";
|
||||
|
||||
import { MarkdownEditor } from "talk-framework/components/loadables";
|
||||
import { parseBool } from "talk-framework/lib/form";
|
||||
import {
|
||||
formatEmpty,
|
||||
parseBool,
|
||||
parseEmptyAsNull,
|
||||
} from "talk-framework/lib/form";
|
||||
import {
|
||||
MessageBox,
|
||||
MessageBoxContent,
|
||||
@@ -54,7 +58,11 @@ const MessageBoxConfig: StatelessComponent<Props> = ({ disabled }) => (
|
||||
</WidthLimitedDescription>
|
||||
</Localized>
|
||||
{input.checked && (
|
||||
<Field name="messageBox.icon">
|
||||
<Field
|
||||
name="messageBox.icon"
|
||||
parse={parseEmptyAsNull}
|
||||
format={formatEmpty}
|
||||
>
|
||||
{({ input: iconInput }) => (
|
||||
<Field name="messageBox.content">
|
||||
{({ input: contentInput, meta }) => (
|
||||
|
||||
+1
-1
@@ -40,7 +40,7 @@ exports[`renders comment stream with community guidelines 1`] = `
|
||||
</button>
|
||||
</div>
|
||||
<div
|
||||
className="CallOut-root CallOut-colorPrimary"
|
||||
className="CallOut-root CallOut-colorPrimary CallOut-fullWidth"
|
||||
>
|
||||
<div
|
||||
className="Markdown-root"
|
||||
|
||||
@@ -0,0 +1,139 @@
|
||||
import RTE from "@coralproject/rte";
|
||||
import { pureMerge } from "talk-common/utils";
|
||||
import { GQLResolver } from "talk-framework/schema";
|
||||
import {
|
||||
createResolversStub,
|
||||
CreateTestRendererParams,
|
||||
findParentWithType,
|
||||
waitForElement,
|
||||
within,
|
||||
} from "talk-framework/testHelpers";
|
||||
|
||||
import {
|
||||
baseComment,
|
||||
commenters,
|
||||
comments,
|
||||
settings,
|
||||
stories,
|
||||
} from "../fixtures";
|
||||
import create from "./create";
|
||||
|
||||
const commentFixture = comments[0];
|
||||
|
||||
const storyFixture = {
|
||||
...stories[0],
|
||||
comments: {
|
||||
pageInfo: {
|
||||
hasNextPage: false,
|
||||
},
|
||||
edges: [
|
||||
{
|
||||
node: commentFixture,
|
||||
cursor: commentFixture.createdAt,
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
const createTestRenderer = async (
|
||||
params: CreateTestRendererParams<GQLResolver> = {}
|
||||
) => {
|
||||
const { testRenderer, context } = create({
|
||||
...params,
|
||||
resolvers: pureMerge(
|
||||
createResolversStub<GQLResolver>({
|
||||
Query: {
|
||||
settings: () => settings,
|
||||
viewer: () => commenters[0],
|
||||
comment: ({ variables }) => {
|
||||
expectAndFail(variables.id).toBe(commentFixture.id);
|
||||
return commentFixture;
|
||||
},
|
||||
story: ({ variables }) => {
|
||||
expectAndFail(variables.id).toBe(storyFixture.id);
|
||||
return storyFixture;
|
||||
},
|
||||
},
|
||||
}),
|
||||
params.resolvers
|
||||
),
|
||||
initLocalState: (localRecord, source, environment) => {
|
||||
localRecord.setValue(storyFixture.id, "storyID");
|
||||
localRecord.setValue(commentFixture.id, "commentID");
|
||||
localRecord.setValue(true, "loggedIn");
|
||||
if (params.initLocalState) {
|
||||
params.initLocalState(localRecord, source, environment);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
const comment = await waitForElement(() =>
|
||||
within(testRenderer.root).getByTestID("comment-comment-0")
|
||||
);
|
||||
|
||||
// Open reply form.
|
||||
within(comment)
|
||||
.getByText("Reply", { selector: "button" })
|
||||
.props.onClick();
|
||||
|
||||
const rte = await waitForElement(
|
||||
() =>
|
||||
findParentWithType(
|
||||
within(comment).getByLabelText("Write a reply"),
|
||||
// We'll use the RTE component here as an exception because the
|
||||
// jsdom does not support all of what is needed for rendering the
|
||||
// Rich Text Editor.
|
||||
RTE
|
||||
)!
|
||||
);
|
||||
|
||||
const form = findParentWithType(rte, "form")!;
|
||||
return {
|
||||
testRenderer,
|
||||
context,
|
||||
comment,
|
||||
rte,
|
||||
form,
|
||||
};
|
||||
};
|
||||
|
||||
it("post a reply", async () => {
|
||||
const { testRenderer, rte, form } = await createTestRenderer({
|
||||
resolvers: createResolversStub<GQLResolver>({
|
||||
Mutation: {
|
||||
createCommentReply: ({ variables }) => {
|
||||
expectAndFail(variables).toMatchObject({
|
||||
storyID: storyFixture.id,
|
||||
parentID: storyFixture.comments.edges[0].node.id,
|
||||
parentRevisionID: storyFixture.comments.edges[0].node.revision.id,
|
||||
body: "<b>Hello world!</b>",
|
||||
});
|
||||
return {
|
||||
edge: {
|
||||
cursor: "",
|
||||
node: {
|
||||
...baseComment,
|
||||
id: "comment-x",
|
||||
author: commenters[0],
|
||||
body: "<b>Hello world! (from server)</b>",
|
||||
},
|
||||
},
|
||||
};
|
||||
},
|
||||
},
|
||||
}),
|
||||
});
|
||||
|
||||
// Write reply .
|
||||
rte.props.onChange({ html: "<b>Hello world!</b>" });
|
||||
form.props.onSubmit();
|
||||
|
||||
const commentReplyList = within(testRenderer.root).getByTestID(
|
||||
"commentReplyList-comment-0"
|
||||
);
|
||||
|
||||
// Test after server response.
|
||||
await waitForElement(() =>
|
||||
within(commentReplyList).getByText("(from server)", { exact: false })
|
||||
);
|
||||
});
|
||||
@@ -18,7 +18,7 @@ import { baseComment, commenters, settings, stories } from "../fixtures";
|
||||
import create from "./create";
|
||||
|
||||
async function createTestRenderer(
|
||||
resolver: any,
|
||||
resolver: any = {},
|
||||
options: { muteNetworkErrors?: boolean } = {}
|
||||
) {
|
||||
const resolvers = {
|
||||
@@ -50,9 +50,11 @@ async function createTestRenderer(
|
||||
);
|
||||
|
||||
// Open reply form.
|
||||
within(comment)
|
||||
.getByText("Reply", { selector: "button" })
|
||||
.props.onClick();
|
||||
const replyButton = within(comment).getByText("Reply", {
|
||||
selector: "button",
|
||||
});
|
||||
|
||||
replyButton.props.onClick();
|
||||
|
||||
const rte = await waitForElement(
|
||||
() =>
|
||||
@@ -70,11 +72,18 @@ async function createTestRenderer(
|
||||
testRenderer,
|
||||
context,
|
||||
comment,
|
||||
replyButton,
|
||||
rte,
|
||||
form,
|
||||
};
|
||||
}
|
||||
|
||||
it("hides form when reclicking on the reply button", async () => {
|
||||
const { comment, replyButton } = await createTestRenderer();
|
||||
replyButton.props.onClick();
|
||||
expect(within(comment).queryByLabelText("Write a reply")).toBeNull();
|
||||
});
|
||||
|
||||
it("post a reply", async () => {
|
||||
const { testRenderer, comment, rte, form } = await createTestRenderer({
|
||||
Mutation: {
|
||||
|
||||
@@ -1,9 +1,14 @@
|
||||
import { cloneDeep } from "lodash";
|
||||
import sinon from "sinon";
|
||||
|
||||
import { pureMerge } from "talk-common/utils";
|
||||
import {
|
||||
GQLResolver,
|
||||
MutationToUpdateStorySettingsResolver,
|
||||
} from "talk-framework/schema";
|
||||
import {
|
||||
createMutationResolverStub,
|
||||
createResolversStub,
|
||||
CreateTestRendererParams,
|
||||
findParentWithType,
|
||||
replaceHistoryLocation,
|
||||
wait,
|
||||
waitForElement,
|
||||
within,
|
||||
@@ -12,30 +17,36 @@ import {
|
||||
import { moderators, settings, stories } from "../fixtures";
|
||||
import create from "./create";
|
||||
|
||||
async function createTestRenderer(
|
||||
resolver: any = {},
|
||||
options: { muteNetworkErrors?: boolean; status?: string } = {}
|
||||
) {
|
||||
const resolvers = {
|
||||
Query: {
|
||||
settings: sinon.stub().returns(settings),
|
||||
story: sinon.stub().callsFake((_: any, variables: any) => {
|
||||
expectAndFail(variables).toEqual({ id: stories[0].id, url: null });
|
||||
return stories[0];
|
||||
}),
|
||||
viewer: sinon.stub().returns(moderators[0]),
|
||||
...resolver.Query,
|
||||
},
|
||||
...resolver,
|
||||
};
|
||||
const viewer = moderators[0];
|
||||
const story = stories[0];
|
||||
|
||||
beforeEach(async () => {
|
||||
replaceHistoryLocation("http://localhost/admin/community");
|
||||
});
|
||||
|
||||
const createTestRenderer = async (
|
||||
params: CreateTestRendererParams<GQLResolver> = {}
|
||||
) => {
|
||||
const { testRenderer } = create({
|
||||
// Set this to true, to see graphql responses.
|
||||
logNetwork: false,
|
||||
muteNetworkErrors: options.muteNetworkErrors,
|
||||
resolvers,
|
||||
initLocalState: localRecord => {
|
||||
localRecord.setValue(stories[0].id, "storyID");
|
||||
...params,
|
||||
resolvers: pureMerge(
|
||||
createResolversStub<GQLResolver>({
|
||||
Query: {
|
||||
settings: () => settings,
|
||||
story: ({ variables }) => {
|
||||
expectAndFail(variables).toEqual({ id: story.id, url: null });
|
||||
return story;
|
||||
},
|
||||
viewer: () => viewer,
|
||||
},
|
||||
}),
|
||||
params.resolvers
|
||||
),
|
||||
initLocalState: (localRecord, source, environment) => {
|
||||
localRecord.setValue(story.id, "storyID");
|
||||
if (params.initLocalState) {
|
||||
params.initLocalState(localRecord, source, environment);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
@@ -46,24 +57,23 @@ async function createTestRenderer(
|
||||
const form = findParentWithType(applyButton, "form")!;
|
||||
|
||||
return { testRenderer, tabPane, applyButton, form };
|
||||
}
|
||||
};
|
||||
|
||||
it("change premod", async () => {
|
||||
let storyRecord = cloneDeep(stories[0]);
|
||||
const updateStorySettingsStub = sinon
|
||||
.stub()
|
||||
.callsFake((_: any, data: any) => {
|
||||
expectAndFail(data.input.settings.moderation).toEqual("PRE");
|
||||
storyRecord = pureMerge(storyRecord, { settings: data.input.settings });
|
||||
return {
|
||||
story: storyRecord,
|
||||
clientMutationId: data.input.clientMutationId,
|
||||
};
|
||||
});
|
||||
const updateStorySettingsStub = createMutationResolverStub<
|
||||
MutationToUpdateStorySettingsResolver
|
||||
>(({ variables }) => {
|
||||
expectAndFail(variables.settings.moderation).toEqual("PRE");
|
||||
return {
|
||||
story: pureMerge(story, { settings: variables.settings }),
|
||||
};
|
||||
});
|
||||
const { form, applyButton } = await createTestRenderer({
|
||||
Mutation: {
|
||||
updateStorySettings: updateStorySettingsStub,
|
||||
},
|
||||
resolvers: createResolversStub<GQLResolver>({
|
||||
Mutation: {
|
||||
updateStorySettings: updateStorySettingsStub,
|
||||
},
|
||||
}),
|
||||
});
|
||||
|
||||
const premodField = within(form).getByLabelText("Enable Pre-Moderation");
|
||||
@@ -89,21 +99,20 @@ it("change premod", async () => {
|
||||
});
|
||||
|
||||
it("change premod links", async () => {
|
||||
let storyRecord = cloneDeep(stories[0]);
|
||||
const updateStorySettingsStub = sinon
|
||||
.stub()
|
||||
.callsFake((_: any, data: any) => {
|
||||
expectAndFail(data.input.settings.premodLinksEnable).toEqual(true);
|
||||
storyRecord = pureMerge(storyRecord, { settings: data.input.settings });
|
||||
return {
|
||||
story: storyRecord,
|
||||
clientMutationId: data.input.clientMutationId,
|
||||
};
|
||||
});
|
||||
const updateStorySettingsStub = createMutationResolverStub<
|
||||
MutationToUpdateStorySettingsResolver
|
||||
>(({ variables }) => {
|
||||
expectAndFail(variables.settings.premodLinksEnable).toEqual(true);
|
||||
return {
|
||||
story: pureMerge(story, { settings: variables.settings }),
|
||||
};
|
||||
});
|
||||
const { form, applyButton } = await createTestRenderer({
|
||||
Mutation: {
|
||||
updateStorySettings: updateStorySettingsStub,
|
||||
},
|
||||
resolvers: createResolversStub<GQLResolver>({
|
||||
Mutation: {
|
||||
updateStorySettings: updateStorySettingsStub,
|
||||
},
|
||||
}),
|
||||
});
|
||||
|
||||
const premodLinksField = within(form).getByLabelText(
|
||||
@@ -131,25 +140,24 @@ it("change premod links", async () => {
|
||||
});
|
||||
|
||||
it("change message box", async () => {
|
||||
let storyRecord = cloneDeep(stories[0]);
|
||||
const updateStorySettingsStub = sinon
|
||||
.stub()
|
||||
.callsFake((_: any, data: any) => {
|
||||
expectAndFail(data.input.settings.messageBox).toEqual({
|
||||
enabled: true,
|
||||
content: "*What do you think?*",
|
||||
icon: "question_answer",
|
||||
});
|
||||
storyRecord = pureMerge(storyRecord, { settings: data.input.settings });
|
||||
return {
|
||||
story: storyRecord,
|
||||
clientMutationId: data.input.clientMutationId,
|
||||
};
|
||||
const updateStorySettingsStub = createMutationResolverStub<
|
||||
MutationToUpdateStorySettingsResolver
|
||||
>(({ variables }) => {
|
||||
expectAndFail(variables.settings.messageBox).toEqual({
|
||||
enabled: true,
|
||||
content: "*What do you think?*",
|
||||
icon: "question_answer",
|
||||
});
|
||||
return {
|
||||
story: pureMerge(story, { settings: variables.settings }),
|
||||
};
|
||||
});
|
||||
const { form, applyButton } = await createTestRenderer({
|
||||
Mutation: {
|
||||
updateStorySettings: updateStorySettingsStub,
|
||||
},
|
||||
resolvers: createResolversStub<GQLResolver>({
|
||||
Mutation: {
|
||||
updateStorySettings: updateStorySettingsStub,
|
||||
},
|
||||
}),
|
||||
});
|
||||
|
||||
const enableField = within(form).getByLabelText(
|
||||
@@ -162,9 +170,9 @@ it("change message box", async () => {
|
||||
expect(applyButton.props.disabled).toBe(false);
|
||||
|
||||
// Select icon
|
||||
within(form)
|
||||
.getByLabelText("question_answer")
|
||||
.props.onChange({ target: { value: "question_answer" } });
|
||||
const iconButton = within(form).getByLabelText("question_answer");
|
||||
|
||||
iconButton.props.onChange({ target: { value: iconButton.props.value } });
|
||||
|
||||
// Change content.
|
||||
(await waitForElement(() =>
|
||||
@@ -185,3 +193,52 @@ it("change message box", async () => {
|
||||
// Should have successfully sent with server.
|
||||
expect(updateStorySettingsStub.called).toBe(true);
|
||||
});
|
||||
|
||||
it("remove message icon", async () => {
|
||||
const updateStorySettingsStub = createMutationResolverStub<
|
||||
MutationToUpdateStorySettingsResolver
|
||||
>(({ variables }) => {
|
||||
expectAndFail(variables.settings.messageBox).toEqual({
|
||||
enabled: true,
|
||||
content: "*What do you think?*",
|
||||
icon: null,
|
||||
});
|
||||
return {
|
||||
story: pureMerge(story, { settings: variables.settings }),
|
||||
};
|
||||
});
|
||||
const { form, applyButton } = await createTestRenderer({
|
||||
resolvers: createResolversStub<GQLResolver>({
|
||||
Query: {
|
||||
story: () =>
|
||||
pureMerge<typeof story>(story, {
|
||||
settings: {
|
||||
messageBox: {
|
||||
enabled: true,
|
||||
content: "*What do you think?*",
|
||||
icon: "question_answer",
|
||||
},
|
||||
},
|
||||
}),
|
||||
},
|
||||
Mutation: {
|
||||
updateStorySettings: updateStorySettingsStub,
|
||||
},
|
||||
}),
|
||||
});
|
||||
|
||||
// Select icon
|
||||
const noIconButton = within(form).getByLabelText("No Icon");
|
||||
|
||||
noIconButton.props.onChange({ target: { value: noIconButton.props.value } });
|
||||
|
||||
// Send form
|
||||
form.props.onSubmit();
|
||||
|
||||
expect(applyButton.props.disabled).toBe(true);
|
||||
|
||||
// Wait for submission to be finished
|
||||
await wait(() => {
|
||||
expect(updateStorySettingsStub.called).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,14 +1,24 @@
|
||||
import { GQLUSER_ROLE } from "talk-framework/schema";
|
||||
import {
|
||||
GQLComment,
|
||||
GQLCOMMENT_STATUS,
|
||||
GQLMODERATION_MODE,
|
||||
GQLSettings,
|
||||
GQLStory,
|
||||
GQLUser,
|
||||
GQLUSER_ROLE,
|
||||
} from "talk-framework/schema";
|
||||
import {
|
||||
createFixture,
|
||||
createFixtures,
|
||||
denormalizeComment,
|
||||
denormalizeComments,
|
||||
denormalizeStories,
|
||||
denormalizeStory,
|
||||
} from "talk-framework/testHelpers";
|
||||
|
||||
export const settings = {
|
||||
export const settings = createFixture<GQLSettings>({
|
||||
id: "settings",
|
||||
moderation: "POST",
|
||||
moderation: GQLMODERATION_MODE.POST,
|
||||
premodLinksEnable: false,
|
||||
communityGuidelines: {
|
||||
enabled: false,
|
||||
@@ -21,9 +31,8 @@ export const settings = {
|
||||
closeCommenting: {
|
||||
auto: false,
|
||||
message: "Story is closed",
|
||||
timeout: null,
|
||||
timeout: undefined,
|
||||
},
|
||||
closedAt: null,
|
||||
auth: {
|
||||
integrations: {
|
||||
facebook: {
|
||||
@@ -71,9 +80,9 @@ export const settings = {
|
||||
charCount: {
|
||||
enabled: false,
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
export const commenters = [
|
||||
export const commenters = createFixtures<GQLUser>([
|
||||
{
|
||||
id: "user-0",
|
||||
username: "Markus",
|
||||
@@ -94,15 +103,15 @@ export const commenters = [
|
||||
username: "Markus",
|
||||
role: GQLUSER_ROLE.COMMENTER,
|
||||
},
|
||||
];
|
||||
]);
|
||||
|
||||
export const baseComment = {
|
||||
export const baseComment = createFixture<GQLComment>({
|
||||
author: commenters[0],
|
||||
body: "Comment Body",
|
||||
revision: {
|
||||
id: "revision-0",
|
||||
},
|
||||
status: "NONE",
|
||||
status: GQLCOMMENT_STATUS.NONE,
|
||||
createdAt: "2018-07-06T18:24:00.000Z",
|
||||
replies: { edges: [], pageInfo: { endCursor: null, hasNextPage: false } },
|
||||
replyCount: 0,
|
||||
@@ -116,178 +125,188 @@ export const baseComment = {
|
||||
},
|
||||
},
|
||||
tags: [],
|
||||
};
|
||||
|
||||
export const comments = denormalizeComments([
|
||||
{
|
||||
...baseComment,
|
||||
id: "comment-0",
|
||||
author: commenters[0],
|
||||
body: "Joining Too",
|
||||
},
|
||||
{
|
||||
...baseComment,
|
||||
id: "comment-1",
|
||||
author: commenters[1],
|
||||
body: "What's up?",
|
||||
},
|
||||
{
|
||||
...baseComment,
|
||||
id: "comment-2",
|
||||
author: commenters[2],
|
||||
body: "Hey!",
|
||||
},
|
||||
{
|
||||
...baseComment,
|
||||
id: "comment-3",
|
||||
author: commenters[2],
|
||||
body: "Comment Body 3",
|
||||
},
|
||||
{
|
||||
...baseComment,
|
||||
id: "comment-4",
|
||||
author: commenters[2],
|
||||
body: "Comment Body 4",
|
||||
},
|
||||
{
|
||||
...baseComment,
|
||||
id: "comment-5",
|
||||
author: commenters[2],
|
||||
body: "Comment Body 5",
|
||||
},
|
||||
]);
|
||||
|
||||
export const commentWithReplies = denormalizeComment({
|
||||
...baseComment,
|
||||
id: "comment-with-replies",
|
||||
author: commenters[0],
|
||||
body: "I like yoghurt",
|
||||
replies: {
|
||||
edges: [
|
||||
{ node: comments[3], cursor: comments[3].createdAt },
|
||||
{ node: comments[4], cursor: comments[4].createdAt },
|
||||
],
|
||||
pageInfo: {
|
||||
hasNextPage: false,
|
||||
},
|
||||
},
|
||||
replyCount: 2,
|
||||
});
|
||||
|
||||
export const commentWithDeepReplies = denormalizeComment({
|
||||
...baseComment,
|
||||
id: "comment-with-deep-replies",
|
||||
author: commenters[0],
|
||||
body: "I like yoghurt",
|
||||
replies: {
|
||||
edges: [
|
||||
{ node: commentWithReplies, cursor: commentWithReplies.createdAt },
|
||||
{ node: comments[5], cursor: comments[5].createdAt },
|
||||
],
|
||||
pageInfo: {
|
||||
hasNextPage: false,
|
||||
},
|
||||
},
|
||||
replyCount: 2,
|
||||
});
|
||||
|
||||
export const commentWithDeepestReplies = denormalizeComment({
|
||||
...baseComment,
|
||||
id: "comment-with-deepest-replies",
|
||||
body: "body 0",
|
||||
replyCount: 1,
|
||||
replies: {
|
||||
...baseComment.replies,
|
||||
edges: [
|
||||
export const comments = denormalizeComments(
|
||||
createFixtures<GQLComment>(
|
||||
[
|
||||
{
|
||||
cursor: baseComment.createdAt,
|
||||
node: {
|
||||
...baseComment,
|
||||
id: "comment-with-deepest-replies-1",
|
||||
body: "body 1",
|
||||
replyCount: 1,
|
||||
replies: {
|
||||
...baseComment.replies,
|
||||
edges: [
|
||||
{
|
||||
cursor: baseComment.createdAt,
|
||||
node: {
|
||||
...baseComment,
|
||||
id: "comment-with-deepest-replies-2",
|
||||
body: "body 2",
|
||||
replyCount: 1,
|
||||
replies: {
|
||||
...baseComment.replies,
|
||||
edges: [
|
||||
{
|
||||
cursor: baseComment.createdAt,
|
||||
node: {
|
||||
...baseComment,
|
||||
id: "comment-with-deepest-replies-3",
|
||||
body: "body 3",
|
||||
replyCount: 1,
|
||||
replies: {
|
||||
...baseComment.replies,
|
||||
edges: [
|
||||
{
|
||||
cursor: baseComment.createdAt,
|
||||
node: {
|
||||
...baseComment,
|
||||
id: "comment-with-deepest-replies-4",
|
||||
body: "body 4",
|
||||
replyCount: 1,
|
||||
replies: {
|
||||
...baseComment.replies,
|
||||
edges: [
|
||||
{
|
||||
cursor: baseComment.createdAt,
|
||||
node: {
|
||||
...baseComment,
|
||||
id: "comment-with-deepest-replies-5",
|
||||
body: "body 5",
|
||||
replyCount: 1,
|
||||
replies: {
|
||||
...baseComment.replies,
|
||||
edges: [
|
||||
{
|
||||
cursor: baseComment.createdAt,
|
||||
node: {
|
||||
...baseComment,
|
||||
id:
|
||||
"comment-with-deepest-replies-6",
|
||||
body: "body 6",
|
||||
replyCount: 1,
|
||||
replies: {
|
||||
...baseComment.replies,
|
||||
edges: [],
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
id: "comment-0",
|
||||
author: commenters[0],
|
||||
body: "Joining Too",
|
||||
},
|
||||
{
|
||||
id: "comment-1",
|
||||
author: commenters[1],
|
||||
body: "What's up?",
|
||||
},
|
||||
{
|
||||
id: "comment-2",
|
||||
author: commenters[2],
|
||||
body: "Hey!",
|
||||
},
|
||||
{
|
||||
id: "comment-3",
|
||||
author: commenters[2],
|
||||
body: "Comment Body 3",
|
||||
},
|
||||
{
|
||||
id: "comment-4",
|
||||
author: commenters[2],
|
||||
body: "Comment Body 4",
|
||||
},
|
||||
{
|
||||
id: "comment-5",
|
||||
author: commenters[2],
|
||||
body: "Comment Body 5",
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
baseComment
|
||||
)
|
||||
);
|
||||
|
||||
export const baseStory = {
|
||||
export const commentWithReplies = denormalizeComment(
|
||||
createFixture<GQLComment>(
|
||||
{
|
||||
id: "comment-with-replies",
|
||||
author: commenters[0],
|
||||
body: "I like yoghurt",
|
||||
replies: {
|
||||
edges: [
|
||||
{ node: comments[3], cursor: comments[3].createdAt },
|
||||
{ node: comments[4], cursor: comments[4].createdAt },
|
||||
],
|
||||
pageInfo: {
|
||||
hasNextPage: false,
|
||||
},
|
||||
},
|
||||
replyCount: 2,
|
||||
},
|
||||
baseComment
|
||||
)
|
||||
);
|
||||
|
||||
export const commentWithDeepReplies = denormalizeComment(
|
||||
createFixture<GQLComment>(
|
||||
{
|
||||
id: "comment-with-deep-replies",
|
||||
author: commenters[0],
|
||||
body: "I like yoghurt",
|
||||
replies: {
|
||||
edges: [
|
||||
{ node: commentWithReplies, cursor: commentWithReplies.createdAt },
|
||||
{ node: comments[5], cursor: comments[5].createdAt },
|
||||
],
|
||||
pageInfo: {
|
||||
hasNextPage: false,
|
||||
},
|
||||
},
|
||||
replyCount: 2,
|
||||
},
|
||||
baseComment
|
||||
)
|
||||
);
|
||||
|
||||
export const commentWithDeepestReplies = denormalizeComment(
|
||||
createFixture<GQLComment>({
|
||||
...baseComment,
|
||||
id: "comment-with-deepest-replies",
|
||||
body: "body 0",
|
||||
replyCount: 1,
|
||||
replies: {
|
||||
...baseComment.replies,
|
||||
edges: [
|
||||
{
|
||||
cursor: baseComment.createdAt,
|
||||
node: {
|
||||
...baseComment,
|
||||
id: "comment-with-deepest-replies-1",
|
||||
body: "body 1",
|
||||
replyCount: 1,
|
||||
replies: {
|
||||
...baseComment.replies,
|
||||
edges: [
|
||||
{
|
||||
cursor: baseComment.createdAt,
|
||||
node: {
|
||||
...baseComment,
|
||||
id: "comment-with-deepest-replies-2",
|
||||
body: "body 2",
|
||||
replyCount: 1,
|
||||
replies: {
|
||||
...baseComment.replies,
|
||||
edges: [
|
||||
{
|
||||
cursor: baseComment.createdAt,
|
||||
node: {
|
||||
...baseComment,
|
||||
id: "comment-with-deepest-replies-3",
|
||||
body: "body 3",
|
||||
replyCount: 1,
|
||||
replies: {
|
||||
...baseComment.replies,
|
||||
edges: [
|
||||
{
|
||||
cursor: baseComment.createdAt,
|
||||
node: {
|
||||
...baseComment,
|
||||
id: "comment-with-deepest-replies-4",
|
||||
body: "body 4",
|
||||
replyCount: 1,
|
||||
replies: {
|
||||
...baseComment.replies,
|
||||
edges: [
|
||||
{
|
||||
cursor: baseComment.createdAt,
|
||||
node: {
|
||||
...baseComment,
|
||||
id:
|
||||
"comment-with-deepest-replies-5",
|
||||
body: "body 5",
|
||||
replyCount: 1,
|
||||
replies: {
|
||||
...baseComment.replies,
|
||||
edges: [
|
||||
{
|
||||
cursor: baseComment.createdAt,
|
||||
node: {
|
||||
...baseComment,
|
||||
id:
|
||||
"comment-with-deepest-replies-6",
|
||||
body: "body 6",
|
||||
replyCount: 1,
|
||||
replies: {
|
||||
...baseComment.replies,
|
||||
edges: [],
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
})
|
||||
);
|
||||
|
||||
export const baseStory = createFixture<GQLStory>({
|
||||
metadata: {
|
||||
title: "title",
|
||||
},
|
||||
@@ -302,140 +321,165 @@ export const baseStory = {
|
||||
totalVisible: 0,
|
||||
},
|
||||
settings: {
|
||||
moderation: "POST",
|
||||
moderation: GQLMODERATION_MODE.POST,
|
||||
premodLinksEnable: false,
|
||||
messageBox: {
|
||||
enabled: false,
|
||||
},
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
export const moderators = [
|
||||
export const moderators = createFixtures<GQLUser>([
|
||||
{
|
||||
id: "me-as-moderator",
|
||||
username: "Moderator",
|
||||
role: GQLUSER_ROLE.MODERATOR,
|
||||
},
|
||||
];
|
||||
|
||||
export const commentsFromStaff = denormalizeComments([
|
||||
{
|
||||
...baseComment,
|
||||
id: "comment-from-staff-0",
|
||||
author: moderators[0],
|
||||
body: "Joining Too",
|
||||
tags: [{ name: "Staff" }],
|
||||
},
|
||||
]);
|
||||
|
||||
export const stories = denormalizeStories([
|
||||
{
|
||||
...baseStory,
|
||||
id: "story-1",
|
||||
url: "http://localhost/stories/story-1",
|
||||
comments: {
|
||||
edges: [
|
||||
{ node: comments[0], cursor: comments[0].createdAt },
|
||||
{ node: comments[1], cursor: comments[1].createdAt },
|
||||
],
|
||||
pageInfo: {
|
||||
hasNextPage: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
...baseStory,
|
||||
id: "story-2",
|
||||
url: "http://localhost/stories/story-2",
|
||||
comments: {
|
||||
edges: [
|
||||
{ node: comments[2], cursor: comments[2].createdAt },
|
||||
{ node: comments[3], cursor: comments[3].createdAt },
|
||||
],
|
||||
pageInfo: {
|
||||
hasNextPage: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
...baseStory,
|
||||
id: "story-3",
|
||||
url: "http://localhost/stories/story-3",
|
||||
comments: {
|
||||
edges: [
|
||||
{ node: comments[0], cursor: comments[0].createdAt },
|
||||
{ node: commentsFromStaff[0], cursor: commentsFromStaff[0].createdAt },
|
||||
],
|
||||
pageInfo: {
|
||||
hasNextPage: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
]);
|
||||
|
||||
export const storyWithNoComments = denormalizeStory({
|
||||
...baseStory,
|
||||
id: "story-with-no-comments",
|
||||
url: "http://localhost/stories/story-with-no-comments",
|
||||
comments: {
|
||||
edges: [],
|
||||
pageInfo: {
|
||||
hasNextPage: false,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export const storyWithReplies = denormalizeStory({
|
||||
...baseStory,
|
||||
id: "story-with-replies",
|
||||
url: "http://localhost/stories/story-with-replies",
|
||||
comments: {
|
||||
edges: [
|
||||
{ node: comments[0], cursor: comments[0].createdAt },
|
||||
{ node: commentWithReplies, cursor: commentWithReplies.createdAt },
|
||||
],
|
||||
pageInfo: {
|
||||
hasNextPage: false,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export const storyWithDeepReplies = denormalizeStory({
|
||||
...baseStory,
|
||||
id: "story-with-deep-replies",
|
||||
url: "http://localhost/stories/story-with-replies",
|
||||
comments: {
|
||||
edges: [
|
||||
{ node: comments[0], cursor: comments[0].createdAt },
|
||||
export const commentsFromStaff = denormalizeComments(
|
||||
createFixtures<GQLComment>(
|
||||
[
|
||||
{
|
||||
node: commentWithDeepReplies,
|
||||
cursor: commentWithDeepReplies.createdAt,
|
||||
id: "comment-from-staff-0",
|
||||
author: moderators[0],
|
||||
body: "Joining Too",
|
||||
tags: [{ name: "Staff" }],
|
||||
},
|
||||
],
|
||||
pageInfo: {
|
||||
hasNextPage: false,
|
||||
},
|
||||
},
|
||||
});
|
||||
baseComment
|
||||
)
|
||||
);
|
||||
|
||||
export const storyWithDeepestReplies = denormalizeStory({
|
||||
...baseStory,
|
||||
id: "story-with-deepest-replies",
|
||||
url: "http://localhost/stories/story-with-replies",
|
||||
comments: {
|
||||
edges: [
|
||||
export const stories = denormalizeStories(
|
||||
createFixtures<GQLStory>(
|
||||
[
|
||||
{
|
||||
node: commentWithDeepestReplies,
|
||||
cursor: commentWithDeepestReplies.createdAt,
|
||||
id: "story-1",
|
||||
url: "http://localhost/stories/story-1",
|
||||
comments: {
|
||||
edges: [
|
||||
{ node: comments[0], cursor: comments[0].createdAt },
|
||||
{ node: comments[1], cursor: comments[1].createdAt },
|
||||
],
|
||||
pageInfo: {
|
||||
hasNextPage: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "story-2",
|
||||
url: "http://localhost/stories/story-2",
|
||||
comments: {
|
||||
edges: [
|
||||
{ node: comments[2], cursor: comments[2].createdAt },
|
||||
{ node: comments[3], cursor: comments[3].createdAt },
|
||||
],
|
||||
pageInfo: {
|
||||
hasNextPage: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "story-3",
|
||||
url: "http://localhost/stories/story-3",
|
||||
comments: {
|
||||
edges: [
|
||||
{ node: comments[0], cursor: comments[0].createdAt },
|
||||
{
|
||||
node: commentsFromStaff[0],
|
||||
cursor: commentsFromStaff[0].createdAt,
|
||||
},
|
||||
],
|
||||
pageInfo: {
|
||||
hasNextPage: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
pageInfo: {
|
||||
hasNextPage: false,
|
||||
},
|
||||
},
|
||||
});
|
||||
baseStory
|
||||
)
|
||||
);
|
||||
|
||||
export const viewerWithComments = {
|
||||
export const storyWithNoComments = denormalizeStory(
|
||||
createFixture<GQLStory>(
|
||||
{
|
||||
id: "story-with-no-comments",
|
||||
url: "http://localhost/stories/story-with-no-comments",
|
||||
comments: {
|
||||
edges: [],
|
||||
pageInfo: {
|
||||
hasNextPage: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
baseStory
|
||||
)
|
||||
);
|
||||
|
||||
export const storyWithReplies = denormalizeStory(
|
||||
createFixture<GQLStory>(
|
||||
{
|
||||
id: "story-with-replies",
|
||||
url: "http://localhost/stories/story-with-replies",
|
||||
comments: {
|
||||
edges: [
|
||||
{ node: comments[0], cursor: comments[0].createdAt },
|
||||
{ node: commentWithReplies, cursor: commentWithReplies.createdAt },
|
||||
],
|
||||
pageInfo: {
|
||||
hasNextPage: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
baseStory
|
||||
)
|
||||
);
|
||||
|
||||
export const storyWithDeepReplies = denormalizeStory(
|
||||
createFixture<GQLStory>(
|
||||
{
|
||||
id: "story-with-deep-replies",
|
||||
url: "http://localhost/stories/story-with-replies",
|
||||
comments: {
|
||||
edges: [
|
||||
{ node: comments[0], cursor: comments[0].createdAt },
|
||||
{
|
||||
node: commentWithDeepReplies,
|
||||
cursor: commentWithDeepReplies.createdAt,
|
||||
},
|
||||
],
|
||||
pageInfo: {
|
||||
hasNextPage: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
baseStory
|
||||
)
|
||||
);
|
||||
|
||||
export const storyWithDeepestReplies = denormalizeStory(
|
||||
createFixture<GQLStory>(
|
||||
{
|
||||
id: "story-with-deepest-replies",
|
||||
url: "http://localhost/stories/story-with-replies",
|
||||
comments: {
|
||||
edges: [
|
||||
{
|
||||
node: commentWithDeepestReplies,
|
||||
cursor: commentWithDeepestReplies.createdAt,
|
||||
},
|
||||
],
|
||||
pageInfo: {
|
||||
hasNextPage: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
baseStory
|
||||
)
|
||||
);
|
||||
|
||||
export const viewerWithComments = createFixture<GQLUser>({
|
||||
id: "me-with-comments",
|
||||
username: "Markus",
|
||||
role: GQLUSER_ROLE.COMMENTER,
|
||||
@@ -454,4 +498,4 @@ export const viewerWithComments = {
|
||||
hasNextPage: false,
|
||||
},
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
@@ -26,7 +26,7 @@ const TileSelector: StatelessComponent<Props> = props => {
|
||||
const { id, name, value, className, children, onChange } = props;
|
||||
const onItemChange = useCallback(
|
||||
(evt: React.ChangeEvent<HTMLInputElement>) =>
|
||||
onChange && onChange(evt.target.value || null),
|
||||
onChange && onChange(evt.target.value),
|
||||
[onChange]
|
||||
);
|
||||
return (
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { isNull, omitBy } from "lodash";
|
||||
import { Db, MongoError } from "mongodb";
|
||||
import uuid from "uuid";
|
||||
|
||||
@@ -337,7 +336,7 @@ export async function updateStorySettings(
|
||||
// Only update fields that have been updated.
|
||||
const update = {
|
||||
$set: {
|
||||
...omitBy(dotize({ settings: input }, { embedArrays: true }), isNull),
|
||||
...dotize({ settings: input }, { embedArrays: true }),
|
||||
// Always update the updated at time.
|
||||
updatedAt: now,
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user