[next] Tests for new story filter and combo box (#2288)

* test: add tests for changes in stories page

* test: search box and moderate specific story

* fix: remaining snapshot

* fix: test
This commit is contained in:
Kiwi
2019-04-27 01:04:16 +02:00
committed by GitHub
parent b1efdad981
commit 60c8f9a0d5
13 changed files with 764 additions and 159 deletions
@@ -1,131 +1,5 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`navigation bar renders navigation bar (empty queues) 1`] = `
<div
className="SubBar-root"
data-testid="moderate-subBar-container"
>
<div
className="Flex-root SubBar-container Flex-flex Flex-justifyCenter Flex-alignCenter"
>
<nav
className="Navigation-root"
>
<ul
className="Navigation-ul"
>
<li
className="NavigationItem-root"
>
<a
className="NavigationItem-anchor NavigationItem-active"
href="/admin/moderate/reported"
onClick={[Function]}
>
<span
aria-hidden="true"
className="Icon-root Icon-sm"
>
flag
</span>
<span>
reported
</span>
<span
className="Counter-root Counter-colorInherit"
data-testid="moderate-navigation-reported-count"
>
<span
className="Counter-text"
>
0
</span>
</span>
</a>
</li>
<li
className="NavigationItem-root"
>
<a
className="NavigationItem-anchor"
href="/admin/moderate/pending"
onClick={[Function]}
>
<span
aria-hidden="true"
className="Icon-root Icon-sm"
>
access_time
</span>
<span>
Pending
</span>
<span
className="Counter-root Counter-colorInherit"
data-testid="moderate-navigation-pending-count"
>
<span
className="Counter-text"
>
0
</span>
</span>
</a>
</li>
<li
className="NavigationItem-root"
>
<a
className="NavigationItem-anchor"
href="/admin/moderate/unmoderated"
onClick={[Function]}
>
<span
aria-hidden="true"
className="Icon-root Icon-sm"
>
forum
</span>
<span>
unmoderated
</span>
<span
className="Counter-root Counter-colorInherit"
data-testid="moderate-navigation-unmoderated-count"
>
<span
className="Counter-text"
>
0
</span>
</span>
</a>
</li>
<li
className="NavigationItem-root"
>
<a
className="NavigationItem-anchor"
href="/admin/moderate/rejected"
onClick={[Function]}
>
<span
aria-hidden="true"
className="Icon-root Icon-sm"
>
cancel
</span>
<span>
rejected
</span>
</a>
</li>
</ul>
</nav>
</div>
</div>
`;
exports[`rejected queue accepts comment in rejected queue: count should be 1 1`] = `
<span
className="Counter-root Counter-colorInherit"
@@ -1524,6 +1398,260 @@ exports[`reported queue renders reported queue with comments and load more 1`] =
</div>
`;
exports[`search bar all stories active search with no results 1`] = `
<div
aria-expanded={true}
aria-haspopup="listbox"
aria-label="Search or jump to story"
aria-owns="moderate-searchBar-listBox"
className="SubBar-root Bar-root"
data-testid="moderate-searchBar-container"
role="combobox"
>
<div
className="Flex-root SubBar-container Flex-flex Flex-justifyCenter Flex-alignCenter"
>
<div
className="Backdrop-root Backdrop-active Bar-bumpZIndex"
/>
<form
aria-label="Stories"
className="Bar-bumpZIndex"
onMouseDown={[Function]}
onSubmit={[Function]}
role="search"
>
<div
className="Popover-root"
>
<div>
<div
className="Flex-root Field-root Flex-flex Flex-alignStretch"
>
<div
className="Flex-root Field-begin Flex-flex Flex-alignCenter"
>
<span
aria-hidden="true"
className="Icon-root Icon-md Field-searchIcon"
>
search
</span>
<div
className="Field-beginStories"
>
Stories:
</div>
</div>
<input
aria-autocomplete="list"
aria-controls="moderate-searchBar-listBox"
aria-label="Search or jump to story..."
autoComplete="off"
className="Field-input"
name="search"
onBlur={[Function]}
onChange={[Function]}
onFocus={[Function]}
onKeyDown={[Function]}
placeholder="Use quotation marks around each search term (e.g. “team”, “St. Louis”)"
spellCheck={false}
value=""
/>
<div
className="Flex-root Field-end Flex-flex Flex-alignCenter"
>
<button
className="BaseButton-root Field-searchButton"
disabled={true}
onBlur={[Function]}
onFocus={[Function]}
onMouseOut={[Function]}
onMouseOver={[Function]}
onTouchEnd={[Function]}
type="submit"
>
Search
</button>
</div>
</div>
</div>
<div
aria-hidden={false}
aria-labelledby="moderate-searchBar-popover-ariainfo"
id="moderate-searchBar-popover"
role="popup"
>
<div
className="Popover-popover Bar-popover Popover-bottom"
style={
Object {
"left": 0,
"opacity": 0,
"pointerEvents": "none",
"position": "absolute",
"top": 0,
}
}
>
<ul
className="Bar-listBox"
id="moderate-searchBar-listBox"
role="listbox"
>
<ul
aria-labelledby="moderate-searchBar-context-title"
className="Group-root"
id="moderate-searchBar-context"
role="group"
>
<li
className="Group-title"
id="moderate-searchBar-context-title"
>
Currently moderating
</li>
<li
aria-selected={false}
className="Option-root"
id="moderate-searchBar-listBoxOption-0"
onClick={[Function]}
role="option"
>
<a
className="Option-link"
href="/admin/moderate"
tabIndex={-1}
>
<div
className="Option-container"
>
<div
className="Option-title"
>
<div
className="AriaInfo-root"
>
Go to
</div>
<span>
All stories
</span>
</div>
<div
className="Option-details"
/>
</div>
</a>
</li>
</ul>
</ul>
</div>
</div>
</div>
</form>
</div>
</div>
`;
exports[`search bar all stories active search with too many results 1`] = `
<li
aria-selected={false}
className="SeeAllOption-root"
id="moderate-searchBar-listBoxOption-3"
onClick={[Function]}
role="option"
>
<a
className="SeeAllOption-link"
href="/admin/stories?q=InterestingStory"
tabIndex={-1}
>
<span>
See all results
</span>
<span
aria-hidden="true"
className="Icon-root Icon-sm SeeAllOption-icon"
>
arrow_forward
</span>
</a>
</li>
`;
exports[`search bar all stories renders search bar 1`] = `
<div
aria-expanded={false}
aria-haspopup="listbox"
aria-label="Search or jump to story"
aria-owns="moderate-searchBar-listBox"
className="SubBar-root Bar-root"
data-testid="moderate-searchBar-container"
role="combobox"
>
<div
className="Flex-root SubBar-container Flex-flex Flex-justifyCenter Flex-alignCenter"
>
<div
className="Backdrop-root Bar-bumpZIndex"
/>
<form
aria-label="Stories"
className="Bar-bumpZIndex"
onMouseDown={[Function]}
onSubmit={[Function]}
role="search"
>
<div
className="Popover-root"
>
<div>
<div
className="Flex-root Field-root Flex-flex Flex-alignStretch"
>
<div
className="Flex-root Field-begin Flex-flex Flex-alignCenter"
>
<span
aria-hidden="true"
className="Icon-root Icon-md Field-searchIcon"
>
search
</span>
</div>
<input
aria-autocomplete="list"
aria-controls="moderate-searchBar-listBox"
aria-label="Search or jump to story..."
autoComplete="off"
className="Field-input Field-inputWithTitle"
name="search"
onBlur={[Function]}
onChange={[Function]}
onFocus={[Function]}
onKeyDown={[Function]}
placeholder="All stories"
spellCheck={false}
value=""
/>
<div
className="Flex-root Field-end Flex-flex Flex-alignCenter"
/>
</div>
</div>
<div
aria-hidden={true}
aria-labelledby="moderate-searchBar-popover-ariainfo"
id="moderate-searchBar-popover"
role="popup"
/>
</div>
</form>
</div>
</div>
`;
exports[`single comment view accepts single comment 1`] = `
<div
className="Card-root ModerateCard-root"
@@ -1991,3 +2119,129 @@ exports[`single comment view renders single comment view 1`] = `
</div>
</div>
`;
exports[`tab bar renders tab bar (empty queues) 1`] = `
<div
className="SubBar-root"
data-testid="moderate-tabBar-container"
>
<div
className="Flex-root SubBar-container Flex-flex Flex-justifyCenter Flex-alignCenter"
>
<nav
className="Navigation-root"
>
<ul
className="Navigation-ul"
>
<li
className="NavigationItem-root"
>
<a
className="NavigationItem-anchor NavigationItem-active"
href="/admin/moderate/reported"
onClick={[Function]}
>
<span
aria-hidden="true"
className="Icon-root Icon-sm"
>
flag
</span>
<span>
reported
</span>
<span
className="Counter-root Counter-colorInherit"
data-testid="moderate-navigation-reported-count"
>
<span
className="Counter-text"
>
0
</span>
</span>
</a>
</li>
<li
className="NavigationItem-root"
>
<a
className="NavigationItem-anchor"
href="/admin/moderate/pending"
onClick={[Function]}
>
<span
aria-hidden="true"
className="Icon-root Icon-sm"
>
access_time
</span>
<span>
Pending
</span>
<span
className="Counter-root Counter-colorInherit"
data-testid="moderate-navigation-pending-count"
>
<span
className="Counter-text"
>
0
</span>
</span>
</a>
</li>
<li
className="NavigationItem-root"
>
<a
className="NavigationItem-anchor"
href="/admin/moderate/unmoderated"
onClick={[Function]}
>
<span
aria-hidden="true"
className="Icon-root Icon-sm"
>
forum
</span>
<span>
unmoderated
</span>
<span
className="Counter-root Counter-colorInherit"
data-testid="moderate-navigation-unmoderated-count"
>
<span
className="Counter-text"
>
0
</span>
</span>
</a>
</li>
<li
className="NavigationItem-root"
>
<a
className="NavigationItem-anchor"
href="/admin/moderate/rejected"
onClick={[Function]}
>
<span
aria-hidden="true"
className="Icon-root Icon-sm"
>
cancel
</span>
<span>
rejected
</span>
</a>
</li>
</ul>
</nav>
</div>
</div>
`;
@@ -12,20 +12,27 @@ import {
createQueryResolverStub,
createResolversStub,
CreateTestRendererParams,
findParentWithType,
replaceHistoryLocation,
toJSON,
wait,
waitForElement,
waitUntilThrow,
within,
} from "talk-framework/testHelpers";
import { noop } from "lodash";
import { ReactTestRenderer } from "react-test-renderer";
import create from "../create";
import {
emptyModerationQueues,
emptyRejectedComments,
emptyStories,
rejectedComments,
reportedComments,
settings,
stories,
storyConnection,
users,
} from "../fixtures";
@@ -38,7 +45,7 @@ beforeEach(async () => {
async function createTestRenderer(
params: CreateTestRendererParams<GQLResolver> = {}
) {
const { testRenderer } = create({
const { testRenderer, context } = create({
...params,
resolvers: pureMerge(
createResolversStub<GQLResolver>({
@@ -58,21 +65,236 @@ async function createTestRenderer(
}
},
});
return testRenderer;
return { testRenderer, context };
}
describe("navigation bar", () => {
it("renders navigation bar (empty queues)", async () => {
const testRenderer = await createTestRenderer();
describe("search bar", () => {
const openSearchBar = async (testRenderer: ReactTestRenderer) => {
const searchBar = await waitForElement(() =>
within(testRenderer.root).getByTestID("moderate-searchBar-container")
);
const textField = within(searchBar).getByLabelText(
"Search or jump to story..."
);
const form = findParentWithType(textField, "form")!;
textField.props.onFocus({});
return { searchBar, textField, form };
};
describe("all stories", () => {
it("renders search bar", async () => {
const { testRenderer } = await createTestRenderer();
const searchBar = await waitForElement(() =>
within(testRenderer.root).getByTestID("moderate-searchBar-container")
);
expect(within(searchBar).toJSON()).toMatchSnapshot();
});
describe("active", () => {
it("search with no results", async () => {
const query = "InterestingStory";
const { testRenderer } = await createTestRenderer({
resolvers: createResolversStub<GQLResolver>({
Query: {
stories: ({ variables }) => {
expectAndFail(variables.query).toBe(query);
return emptyStories;
},
},
}),
});
const { searchBar, textField, form } = await openSearchBar(
testRenderer
);
expect(within(searchBar).toJSON()).toMatchSnapshot();
// Search for sth.
textField.props.onChange(query);
form.props.onSubmit();
// Ensure no results message is shown.
await wait(() =>
within(searchBar).getByText("No results", { exact: false })
);
// Blurring should close the listbox.
textField.props.onBlur({});
expect(within(searchBar).queryByText("No results")).toBeNull();
});
it("search with actual results", async () => {
const query = "InterestingStory";
const {
testRenderer,
context: { transitionControl },
} = await createTestRenderer({
resolvers: createResolversStub<GQLResolver>({
Query: {
stories: ({ variables }) => {
expectAndFail(variables.query).toBe(query);
return storyConnection;
},
},
}),
});
transitionControl.allowTransition = false;
const { searchBar, textField, form } = await openSearchBar(
testRenderer
);
// Search for sth.
textField.props.onChange(query);
form.props.onSubmit();
const story = storyConnection.edges[0].node;
// Find the story in the search results.
const storyOption = findParentWithType(
await waitForElement(() =>
within(searchBar).getByText(story.metadata!.title!, {
exact: false,
})
),
"li"
)!;
// Go to story.
storyOption.props.onClick({ button: 0, preventDefault: noop });
// Expect a routing request was made to the right url.
expect(transitionControl.history[0].pathname).toBe(
`/admin/moderate/${story.id}`
);
});
it("search with too many results", async () => {
const query = "InterestingStory";
const {
testRenderer,
context: { transitionControl },
} = await createTestRenderer({
resolvers: createResolversStub<GQLResolver>({
Query: {
stories: ({ variables }) => {
expectAndFail(variables.query).toBe(query);
return pureMerge<typeof storyConnection>(storyConnection, {
pageInfo: { hasNextPage: true },
});
},
},
}),
});
transitionControl.allowTransition = false;
const { searchBar, textField, form } = await openSearchBar(
testRenderer
);
// Search for sth.
textField.props.onChange(query);
form.props.onSubmit();
// Find see all options in the search results.
const seeAllOption = findParentWithType(
await waitForElement(() =>
within(searchBar).getByText("See all results", { exact: false })
),
"li"
)!;
expect(within(seeAllOption).toJSON()).toMatchSnapshot();
// Go to story.
seeAllOption.props.onClick({ button: 0, preventDefault: noop });
// Expect a routing request was made to the right url.
expect(transitionControl.history[0].pathname).toBe("/admin/stories");
expect(transitionControl.history[0].search).toBe(`?q=${query}`);
});
});
});
describe("specified story", () => {
beforeEach(() => {
replaceHistoryLocation(
`http://localhost/admin/moderate/${stories[0].id}`
);
});
it("renders search bar", async () => {
const { testRenderer } = await createTestRenderer({
resolvers: createResolversStub<GQLResolver>({
Query: {
story: () => stories[0],
},
}),
});
const searchBar = await waitForElement(() =>
within(testRenderer.root).getByTestID("moderate-searchBar-container")
);
const textField = within(searchBar).getByLabelText(
"Search or jump to story..."
);
expect(textField.props.placeholder).toBe(stories[0].metadata!.title);
});
it("shows moderate all option", async () => {
const {
testRenderer,
context: { transitionControl },
} = await createTestRenderer({
resolvers: createResolversStub<GQLResolver>({
Query: {
story: () => stories[0],
},
}),
});
transitionControl.allowTransition = false;
const { searchBar } = await openSearchBar(testRenderer);
// Find see all options in the search results.
const moderateAllOptions = findParentWithType(
await waitForElement(() =>
within(searchBar).getByText("Moderate all", { exact: false })
),
"li"
)!;
// Activate moderate all.
moderateAllOptions.props.onClick({ button: 0, preventDefault: noop });
// Expect a routing request was made to the right url.
expect(transitionControl.history[0].pathname).toBe("/admin/moderate");
});
});
});
describe("tab bar", () => {
it("renders tab bar (empty queues)", async () => {
const { testRenderer } = await createTestRenderer();
const { getByTestID } = within(testRenderer.root);
await waitForElement(() => getByTestID("moderate-container"));
expect(toJSON(getByTestID("moderate-subBar-container"))).toMatchSnapshot();
expect(toJSON(getByTestID("moderate-tabBar-container"))).toMatchSnapshot();
});
});
describe("moderating specific story", () => {
it("passes storyID to the endpoints", async () => {
replaceHistoryLocation(`http://localhost/admin/moderate/${stories[0].id}`);
await createTestRenderer({
resolvers: createResolversStub<GQLResolver>({
Query: {
moderationQueues: ({ variables }) => {
expectAndFail(variables.storyID).toBe(stories[0].id);
return emptyModerationQueues;
},
comments: ({ variables }) => {
expectAndFail(variables.storyID).toBe(stories[0].id);
return emptyRejectedComments;
},
},
}),
});
});
});
describe("reported queue", () => {
it("renders empty reported queue", async () => {
const testRenderer = await createTestRenderer();
const { testRenderer } = await createTestRenderer();
const { getByTestID } = within(testRenderer.root);
await waitForElement(() => getByTestID("moderate-container"));
@@ -81,14 +303,14 @@ describe("reported queue", () => {
it("renders empty pending queue", async () => {
replaceHistoryLocation("http://localhost/admin/moderate/pending");
const testRenderer = await createTestRenderer();
const { testRenderer } = await createTestRenderer();
const { getByText } = within(testRenderer.root);
await waitForElement(() => getByText("no more pending", { exact: false }));
});
it("renders empty unmoderated queue", async () => {
replaceHistoryLocation("http://localhost/admin/moderate/unmoderated");
const testRenderer = await createTestRenderer();
const { testRenderer } = await createTestRenderer();
const { getByText } = within(testRenderer.root);
await waitForElement(() =>
getByText("comments have been moderated", { exact: false })
@@ -97,7 +319,7 @@ describe("reported queue", () => {
it("renders empty rejected queue", async () => {
replaceHistoryLocation("http://localhost/admin/moderate/rejected");
const testRenderer = await createTestRenderer();
const { testRenderer } = await createTestRenderer();
const { getByText } = within(testRenderer.root);
await waitForElement(() =>
getByText("no rejected comments", { exact: false })
@@ -105,7 +327,7 @@ describe("reported queue", () => {
});
it("renders reported queue with comments", async () => {
const testRenderer = await createTestRenderer({
const { testRenderer } = await createTestRenderer({
resolvers: createResolversStub<GQLResolver>({
Query: {
moderationQueues: () =>
@@ -191,7 +413,7 @@ describe("reported queue", () => {
},
});
const testRenderer = await createTestRenderer({
const { testRenderer } = await createTestRenderer({
resolvers: createResolversStub<GQLResolver>({
Query: {
moderationQueues: () => moderationQueuesStub,
@@ -278,7 +500,7 @@ describe("reported queue", () => {
},
});
const testRenderer = await createTestRenderer({
const { testRenderer } = await createTestRenderer({
resolvers: createResolversStub<GQLResolver>({
Query: {
moderationQueues: () => moderationQueuesStub,
@@ -333,7 +555,7 @@ describe("reported queue", () => {
};
});
const testRenderer = await createTestRenderer({
const { testRenderer } = await createTestRenderer({
resolvers: createResolversStub<GQLResolver>({
Query: {
moderationQueues: () =>
@@ -400,7 +622,7 @@ describe("rejected queue", () => {
});
it("renders rejected queue with comments", async () => {
const testRenderer = await createTestRenderer({
const { testRenderer } = await createTestRenderer({
resolvers: createResolversStub<GQLResolver>({
Query: {
comments: ({ variables }) => {
@@ -435,7 +657,7 @@ describe("rejected queue", () => {
});
it("renders rejected queue with comments and load more", async () => {
const testRenderer = await createTestRenderer({
const { testRenderer } = await createTestRenderer({
resolvers: createResolversStub<GQLResolver>({
Query: {
comments: ({ variables, callCount }) => {
@@ -540,7 +762,7 @@ describe("rejected queue", () => {
};
});
const testRenderer = await createTestRenderer({
const { testRenderer } = await createTestRenderer({
resolvers: createResolversStub<GQLResolver>({
Query: {
comments: ({ variables }) => {
@@ -613,7 +835,7 @@ describe("single comment view", () => {
});
it("renders single comment view", async () => {
const testRenderer = await createTestRenderer({
const { testRenderer } = await createTestRenderer({
resolvers: {
Query: {
comment: commentStub,
@@ -644,7 +866,7 @@ describe("single comment view", () => {
};
});
const testRenderer = await createTestRenderer({
const { testRenderer } = await createTestRenderer({
resolvers: {
Query: {
comment: commentStub,
@@ -683,7 +905,7 @@ describe("single comment view", () => {
};
});
const testRenderer = await createTestRenderer({
const { testRenderer } = await createTestRenderer({
resolvers: {
Query: {
comment: commentStub,
@@ -1,3 +1,4 @@
import { noop } from "lodash";
import TestRenderer from "react-test-renderer";
import { pureMerge } from "talk-common/utils";
@@ -36,7 +37,7 @@ beforeEach(async () => {
async function createTestRenderer(
params: CreateTestRendererParams<GQLResolver> = {}
) {
const { testRenderer } = create({
const { testRenderer, context } = create({
...params,
resolvers: pureMerge(
createResolversStub<GQLResolver>({
@@ -62,7 +63,7 @@ async function createTestRenderer(
const container = await waitForElement(() =>
within(testRenderer.root).getByTestID("stories-container")
);
return { testRenderer, container };
return { testRenderer, container, context };
}
it("renders stories", async () => {
@@ -81,6 +82,26 @@ it("renders empty stories", async () => {
expect(within(container).toJSON()).toMatchSnapshot();
});
it("goes to moderation when clicking on title", async () => {
const {
container,
context: { transitionControl },
} = await createTestRenderer();
// Prevent router transitions.
transitionControl.allowTransition = false;
const story = storyConnection.edges[0].node;
within(container)
.getByText(story.metadata!.title!)
.props.onClick({ button: 0, preventDefault: noop });
// Expect a routing request was made to the right url.
expect(transitionControl.history[0].pathname).toBe(
`/admin/moderate/${story.id}`
);
});
it("filter by status", async () => {
const { container } = await createTestRenderer({
resolvers: createResolversStub<GQLResolver>({
@@ -268,3 +289,24 @@ it("filter by search", async () => {
within(container).getByText("could not find any", { exact: false })
);
});
it("use searchFilter from url", async () => {
const searchFilter = "CandyMountain";
replaceHistoryLocation(`http://localhost/admin/stories?q=${searchFilter}`);
const { container } = await createTestRenderer({
resolvers: createResolversStub<GQLResolver>({
Query: {
stories: ({ variables }) => {
expectAndFail(variables.query).toBe(searchFilter);
return emptyStories;
},
},
}),
});
const searchField = within(container).getByLabelText(
"Search by story title",
{ exact: false }
);
expect(searchField.props.value).toBe(searchFilter);
});