Add integration test

This commit is contained in:
Chi Vinh Le
2018-08-31 17:15:40 +02:00
parent 428c35a931
commit 25e8cb517b
8 changed files with 717 additions and 19 deletions
+25 -15
View File
@@ -1580,6 +1580,15 @@
"lodash.deburr": "^4.1.0"
}
},
"@sinonjs/commons": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.0.2.tgz",
"integrity": "sha512-WR3dlgqJP4QNrLC4iXN/5/2WaLQQ0VijOOkmflqFGVJ6wLEpbSjo7c0ZeGIdtY8Crk7xBBp87sM6+Mkerz7alw==",
"dev": true,
"requires": {
"type-detect": "4.0.8"
}
},
"@sinonjs/formatio": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/@sinonjs/formatio/-/formatio-2.0.0.tgz",
@@ -15230,9 +15239,9 @@
"dev": true
},
"just-extend": {
"version": "1.1.27",
"resolved": "https://registry.npmjs.org/just-extend/-/just-extend-1.1.27.tgz",
"integrity": "sha512-mJVp13Ix6gFo3SBAy9U/kL+oeZqzlYYYLQBwXVBlVzIsZwBqGREnOro24oC/8s8aox+rJhtZ2DiQof++IrkA+g==",
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/just-extend/-/just-extend-3.0.0.tgz",
"integrity": "sha512-Fu3T6pKBuxjWT/p4DkqGHFRsysc8OauWr4ZRTY9dIx07Y9O0RkoR5jcv28aeD1vuAwhm3nLkDurwLXoALp4DpQ==",
"dev": true
},
"jwa": {
@@ -16780,13 +16789,13 @@
"dev": true
},
"nise": {
"version": "1.4.2",
"resolved": "https://registry.npmjs.org/nise/-/nise-1.4.2.tgz",
"integrity": "sha512-BxH/DxoQYYdhKgVAfqVy4pzXRZELHOIewzoesxpjYvpU+7YOalQhGNPf7wAx8pLrTNPrHRDlLOkAl8UI0ZpXjw==",
"version": "1.4.4",
"resolved": "https://registry.npmjs.org/nise/-/nise-1.4.4.tgz",
"integrity": "sha512-pxE0c9PzgrUTyhfv5p+5eMIdfU2bLEsq8VQEuE0kxM4zP7SujSar7rk9wpI2F7RyyCEvLyj5O7Is3RER5F36Fg==",
"dev": true,
"requires": {
"@sinonjs/formatio": "^2.0.0",
"just-extend": "^1.1.27",
"just-extend": "^3.0.0",
"lolex": "^2.3.2",
"path-to-regexp": "^1.7.0",
"text-encoding": "^0.6.4"
@@ -22515,25 +22524,26 @@
"dev": true
},
"sinon": {
"version": "6.1.3",
"resolved": "https://registry.npmjs.org/sinon/-/sinon-6.1.3.tgz",
"integrity": "sha512-yeTza8xIZZdiXntCHJAzKll/sSYE+DuJOS8hiSapzaLqdW8eCNVVC9je9SZYYTkPm2bLts9x6UYxwuMAVVrM6Q==",
"version": "6.1.5",
"resolved": "https://registry.npmjs.org/sinon/-/sinon-6.1.5.tgz",
"integrity": "sha512-TcbRoWs1SdY6NOqfj0c9OEQquBoZH+qEf8799m1jjcbfWrrpyCQ3B/BpX7+NKa7Vn33Jl+Z50H4Oys3bzygK2Q==",
"dev": true,
"requires": {
"@sinonjs/commons": "^1.0.1",
"@sinonjs/formatio": "^2.0.0",
"@sinonjs/samsam": "^2.0.0",
"diff": "^3.5.0",
"lodash.get": "^4.4.2",
"lolex": "^2.4.2",
"nise": "^1.3.3",
"lolex": "^2.7.1",
"nise": "^1.4.2",
"supports-color": "^5.4.0",
"type-detect": "^4.0.8"
},
"dependencies": {
"supports-color": {
"version": "5.4.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz",
"integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==",
"version": "5.5.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
"integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
"dev": true,
"requires": {
"has-flag": "^3.0.0"
+1 -1
View File
@@ -207,7 +207,7 @@
"relay-runtime": "^1.7.0-rc.1",
"sane": "^2.5.2",
"simulant": "^0.2.2",
"sinon": "^6.1.3",
"sinon": "^6.1.5",
"style-loader": "^0.21.0",
"ts-jest": "^23.0.0",
"ts-loader": "^4.4.2",
@@ -0,0 +1,9 @@
import sinon, { SinonStub } from "sinon";
export default function createSinonStub(
...callbacks: Array<(s: SinonStub) => void>
): SinonStub {
const stub = sinon.stub();
callbacks.forEach(cb => cb(stub));
return stub;
}
@@ -3,6 +3,7 @@ export {
CreateRelayEnvironmentParams,
} from "./createRelayEnvironment";
export { default as createFluentBundle } from "./createFluentBundle";
export { default as createSinonStub } from "./createSinonStub";
export {
default as removeFragmentRefs,
NoFragmentRefs,
@@ -27,13 +27,21 @@ export interface PostCommentFormProps {
const PostCommentForm: StatelessComponent<PostCommentFormProps> = props => (
<Form onSubmit={props.onSubmit}>
{({ handleSubmit, submitting }) => (
<form autoComplete="off" onSubmit={handleSubmit} className={styles.root}>
<form
autoComplete="off"
onSubmit={handleSubmit}
className={styles.root}
id="comments-postCommentForm-form"
>
<HorizontalGutter>
<Field name="body" validate={required}>
{({ input, meta }) => (
<div>
<Localized id="comments-postCommentForm-rteLabel">
<AriaInfo component="label" htmlFor="postCommentField">
<AriaInfo
component="label"
htmlFor="comments-postCommentForm-field"
>
Post a comment
</AriaInfo>
</Localized>
@@ -42,7 +50,7 @@ const PostCommentForm: StatelessComponent<PostCommentFormProps> = props => (
attrs={{ placeholder: true }}
>
<RTE
inputId="postCommentField"
inputId="comments-postCommentForm-field"
onChange={({ html }) => input.onChange(html)}
value={input.value}
placeholder="Post a comment"
@@ -0,0 +1,557 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`post a comment 1`] = `
<div
className="Flex-root App-root Flex-flex Flex-justifyCenter"
>
<div
className="HorizontalGutter-root Stream-root HorizontalGutter-double"
>
<div
className="HorizontalGutter-root HorizontalGutter-half"
>
<div
className="Flex-root"
>
<div
className="Flex-flex Flex-halfItemGutter Flex-wrap"
>
<div
className="Typography-root Typography-bodyCopy Typography-colorTextPrimary"
>
Signed in as
<span
className="Typography-root Typography-bodyCopyBold Typography-colorTextPrimary"
>
Markus
</span>
.
</div>
<div
className="Flex-root Typography-root Typography-bodyCopy Typography-colorTextPrimary Flex-flex"
>
<span>
Not you? 
</span>
<button
className="BaseButton-root Button-root Button-sizeSmall Button-colorPrimary Button-variantUnderlined"
onBlur={[Function]}
onClick={[Function]}
onFocus={[Function]}
onMouseDown={[Function]}
onMouseOut={[Function]}
onMouseOver={[Function]}
onTouchEnd={[Function]}
type="button"
>
Sign Out
</button>
</div>
</div>
</div>
<form
autoComplete="off"
className="PostCommentForm-root"
id="comments-postCommentForm-form"
onSubmit={[Function]}
>
<div
className="HorizontalGutter-root HorizontalGutter-full"
>
<div>
<label
className="AriaInfo-root"
htmlFor="comments-postCommentForm-field"
>
Post a comment
</label>
<div>
<div
className=""
>
<div
className="Toolbar-toolbar"
>
<button
className="Button-button"
disabled={false}
onClick={[Function]}
title="Bold"
type="button"
>
<span
aria-hidden="true"
className="Icon-root Icon-sm"
>
format_bold
</span>
</button>
<button
className="Button-button"
disabled={false}
onClick={[Function]}
title="Italic"
type="button"
>
<span
aria-hidden="true"
className="Icon-root Icon-sm"
>
format_italic
</span>
</button>
<button
className="Button-button"
disabled={false}
onClick={[Function]}
title="Blockquote"
type="button"
>
<span
aria-hidden="true"
className="Icon-root Icon-sm"
>
format_quote
</span>
</button>
</div>
<div
aria-hidden="true"
className="RTE-placeholder RTE-placeholder"
>
Post a comment
</div>
<div
aria-placeholder="Post a comment"
className="RTE-contentEditable RTE-content"
contentEditable={true}
dangerouslySetInnerHTML={
Object {
"__html": "",
}
}
id="comments-postCommentForm-field"
onBlur={[Function]}
onChange={[Function]}
onCut={[Function]}
onFocus={[Function]}
onInput={[Function]}
onKeyDown={[Function]}
onPaste={[Function]}
onSelect={[Function]}
/>
</div>
</div>
</div>
<div
className="Flex-root Flex-flex Flex-justifySpaceBetween Flex-alignFlexStart Flex-directionRow"
>
<div
className="PostCommentForm-poweredBy"
>
<span
className="Typography-root Typography-inputDescription Typography-colorTextSecondary"
>
Powered by
<span
className="Typography-root Typography-heading4 Typography-colorTextPrimary"
>
The Coral Project
</span>
</span>
</div>
<button
className="BaseButton-root Button-root Button-sizeRegular Button-colorPrimary Button-variantFilled"
disabled={false}
onBlur={[Function]}
onFocus={[Function]}
onMouseDown={[Function]}
onMouseOut={[Function]}
onMouseOver={[Function]}
onTouchEnd={[Function]}
type="submit"
>
Submit
</button>
</div>
</div>
</form>
</div>
<div
aria-live="polite"
className="HorizontalGutter-root HorizontalGutter-full"
id="talk-comments-stream-log"
role="log"
>
<div
className="HorizontalGutter-root HorizontalGutter-full"
>
<div
className="Comment-root"
role="article"
>
<div
className="Flex-root TopBar-root Flex-flex Flex-halfItemGutter Flex-alignBaseline Flex-directionColumn"
>
<span
className="Typography-root Typography-heading3 Typography-colorTextPrimary Username-root"
>
Markus
</span>
<time
className="Timestamp-root RelativeTime-root"
dateTime="2018-07-06T18:24:00.000Z"
title="2018-07-06T18:24:00.000Z"
>
2018-07-06T18:24:00.000Z
</time>
</div>
<div
className="HTMLContent-root"
dangerouslySetInnerHTML={
Object {
"__html": "<strong>Hello world</strong>",
}
}
/>
<div
className="Comment-footer"
/>
</div>
</div>
<div
className="HorizontalGutter-root HorizontalGutter-full"
>
<div
className="Comment-root"
role="article"
>
<div
className="Flex-root TopBar-root Flex-flex Flex-halfItemGutter Flex-alignBaseline Flex-directionColumn"
>
<span
className="Typography-root Typography-heading3 Typography-colorTextPrimary Username-root"
>
Markus
</span>
<time
className="Timestamp-root RelativeTime-root"
dateTime="2018-07-06T18:24:00.000Z"
title="2018-07-06T18:24:00.000Z"
>
2018-07-06T18:24:00.000Z
</time>
</div>
<div
className="HTMLContent-root"
dangerouslySetInnerHTML={
Object {
"__html": "Joining Too",
}
}
/>
<div
className="Comment-footer"
/>
</div>
</div>
<div
className="HorizontalGutter-root HorizontalGutter-full"
>
<div
className="Comment-root"
role="article"
>
<div
className="Flex-root TopBar-root Flex-flex Flex-halfItemGutter Flex-alignBaseline Flex-directionColumn"
>
<span
className="Typography-root Typography-heading3 Typography-colorTextPrimary Username-root"
>
Lukas
</span>
<time
className="Timestamp-root RelativeTime-root"
dateTime="2018-07-06T18:20:00.000Z"
title="2018-07-06T18:20:00.000Z"
>
2018-07-06T18:20:00.000Z
</time>
</div>
<div
className="HTMLContent-root"
dangerouslySetInnerHTML={
Object {
"__html": "What's up?",
}
}
/>
<div
className="Comment-footer"
/>
</div>
</div>
</div>
</div>
</div>
`;
exports[`renders comment stream 1`] = `
<div
className="Flex-root App-root Flex-flex Flex-justifyCenter"
>
<div
className="HorizontalGutter-root Stream-root HorizontalGutter-double"
>
<div
className="HorizontalGutter-root HorizontalGutter-half"
>
<div
className="Flex-root"
>
<div
className="Flex-flex Flex-halfItemGutter Flex-wrap"
>
<div
className="Typography-root Typography-bodyCopy Typography-colorTextPrimary"
>
Signed in as
<span
className="Typography-root Typography-bodyCopyBold Typography-colorTextPrimary"
>
Markus
</span>
.
</div>
<div
className="Flex-root Typography-root Typography-bodyCopy Typography-colorTextPrimary Flex-flex"
>
<span>
Not you? 
</span>
<button
className="BaseButton-root Button-root Button-sizeSmall Button-colorPrimary Button-variantUnderlined"
onBlur={[Function]}
onClick={[Function]}
onFocus={[Function]}
onMouseDown={[Function]}
onMouseOut={[Function]}
onMouseOver={[Function]}
onTouchEnd={[Function]}
type="button"
>
Sign Out
</button>
</div>
</div>
</div>
<form
autoComplete="off"
className="PostCommentForm-root"
id="comments-postCommentForm-form"
onSubmit={[Function]}
>
<div
className="HorizontalGutter-root HorizontalGutter-full"
>
<div>
<label
className="AriaInfo-root"
htmlFor="comments-postCommentForm-field"
>
Post a comment
</label>
<div>
<div
className=""
>
<div
className="Toolbar-toolbar"
>
<button
className="Button-button"
disabled={false}
onClick={[Function]}
title="Bold"
type="button"
>
<span
aria-hidden="true"
className="Icon-root Icon-sm"
>
format_bold
</span>
</button>
<button
className="Button-button"
disabled={false}
onClick={[Function]}
title="Italic"
type="button"
>
<span
aria-hidden="true"
className="Icon-root Icon-sm"
>
format_italic
</span>
</button>
<button
className="Button-button"
disabled={false}
onClick={[Function]}
title="Blockquote"
type="button"
>
<span
aria-hidden="true"
className="Icon-root Icon-sm"
>
format_quote
</span>
</button>
</div>
<div
aria-hidden="true"
className="RTE-placeholder RTE-placeholder"
>
Post a comment
</div>
<div
aria-placeholder="Post a comment"
className="RTE-contentEditable RTE-content"
contentEditable={true}
dangerouslySetInnerHTML={
Object {
"__html": "",
}
}
id="comments-postCommentForm-field"
onBlur={[Function]}
onChange={[Function]}
onCut={[Function]}
onFocus={[Function]}
onInput={[Function]}
onKeyDown={[Function]}
onPaste={[Function]}
onSelect={[Function]}
/>
</div>
</div>
</div>
<div
className="Flex-root Flex-flex Flex-justifySpaceBetween Flex-alignFlexStart Flex-directionRow"
>
<div
className="PostCommentForm-poweredBy"
>
<span
className="Typography-root Typography-inputDescription Typography-colorTextSecondary"
>
Powered by
<span
className="Typography-root Typography-heading4 Typography-colorTextPrimary"
>
The Coral Project
</span>
</span>
</div>
<button
className="BaseButton-root Button-root Button-sizeRegular Button-colorPrimary Button-variantFilled"
disabled={false}
onBlur={[Function]}
onFocus={[Function]}
onMouseDown={[Function]}
onMouseOut={[Function]}
onMouseOver={[Function]}
onTouchEnd={[Function]}
type="submit"
>
Submit
</button>
</div>
</div>
</form>
</div>
<div
aria-live="polite"
className="HorizontalGutter-root HorizontalGutter-full"
id="talk-comments-stream-log"
role="log"
>
<div
className="HorizontalGutter-root HorizontalGutter-full"
>
<div
className="Comment-root"
role="article"
>
<div
className="Flex-root TopBar-root Flex-flex Flex-halfItemGutter Flex-alignBaseline Flex-directionColumn"
>
<span
className="Typography-root Typography-heading3 Typography-colorTextPrimary Username-root"
>
Markus
</span>
<time
className="Timestamp-root RelativeTime-root"
dateTime="2018-07-06T18:24:00.000Z"
title="2018-07-06T18:24:00.000Z"
>
2018-07-06T18:24:00.000Z
</time>
</div>
<div
className="HTMLContent-root"
dangerouslySetInnerHTML={
Object {
"__html": "Joining Too",
}
}
/>
<div
className="Comment-footer"
/>
</div>
</div>
<div
className="HorizontalGutter-root HorizontalGutter-full"
>
<div
className="Comment-root"
role="article"
>
<div
className="Flex-root TopBar-root Flex-flex Flex-halfItemGutter Flex-alignBaseline Flex-directionColumn"
>
<span
className="Typography-root Typography-heading3 Typography-colorTextPrimary Username-root"
>
Lukas
</span>
<time
className="Timestamp-root RelativeTime-root"
dateTime="2018-07-06T18:20:00.000Z"
title="2018-07-06T18:20:00.000Z"
>
2018-07-06T18:20:00.000Z
</time>
</div>
<div
className="HTMLContent-root"
dangerouslySetInnerHTML={
Object {
"__html": "What's up?",
}
}
/>
<div
className="Comment-footer"
/>
</div>
</div>
</div>
</div>
</div>
`;
@@ -0,0 +1,104 @@
import React from "react";
import TestRenderer, { ReactTestRenderer } from "react-test-renderer";
import { RecordProxy } from "relay-runtime";
import { timeout } from "talk-common/utils";
import { TalkContext, TalkContextProvider } from "talk-framework/lib/bootstrap";
import { PostMessageService } from "talk-framework/lib/postMessage";
import { RestClient } from "talk-framework/lib/rest";
import { createInMemoryStorage } from "talk-framework/lib/storage";
import { createSinonStub } from "talk-framework/testHelpers";
import AppContainer from "talk-stream/containers/AppContainer";
import createEnvironment from "./createEnvironment";
import createFluentBundle from "./createFluentBundle";
import createNodeMock from "./createNodeMock";
import { assets, users } from "./fixtures";
let testRenderer: ReactTestRenderer;
beforeEach(() => {
const resolvers = {
Query: {
asset: createSinonStub(
s => s.throws(),
s => s.withArgs(undefined, { id: assets[0].id }).returns(assets[0])
),
me: createSinonStub(
s => s.throws(),
s => s.withArgs(undefined, { clientAuthRevision: 0 }).returns(users[0])
),
},
Mutation: {
createComment: createSinonStub(
s => s.throws(),
s =>
s
.withArgs(undefined, {
input: {
assetID: assets[0].id,
body: "<strong>Hello world!</strong>",
clientMutationId: "0",
},
})
.returns({
commentEdge: {
cursor: "2018-07-06T18:24:00.000Z",
node: {
id: "comment-x",
author: users[0],
body: "<strong>Hello world</strong>",
createdAt: "2018-07-06T18:24:00.000Z",
},
},
clientMutationId: "0",
})
),
},
};
const environment = createEnvironment({
// Set this to true, to see graphql responses.
logNetwork: false,
resolvers,
initLocalState: (localRecord: RecordProxy) => {
localRecord.setValue(assets[0].id, "assetID");
localRecord.setValue(0, "authRevision");
},
});
const context: TalkContext = {
relayEnvironment: environment,
localeBundles: [createFluentBundle()],
localStorage: createInMemoryStorage(),
sessionStorage: createInMemoryStorage(),
rest: new RestClient("http://localhost/api"),
postMessage: new PostMessageService(),
};
testRenderer = TestRenderer.create(
<TalkContextProvider value={context}>
<AppContainer />
</TalkContextProvider>,
{ createNodeMock }
);
});
it("renders comment stream", async () => {
// Wait for loading.
await timeout();
expect(testRenderer.toJSON()).toMatchSnapshot();
});
it("post a comment", async () => {
testRenderer.root
.findByProps({ inputId: "comments-postCommentForm-field" })
.props.onChange({ html: "<strong>Hello world!</strong>" });
testRenderer.root
.findByProps({ id: "comments-postCommentForm-form" })
.props.onSubmit();
// Wait for loading.
await timeout();
expect(testRenderer.toJSON()).toMatchSnapshot();
});
+9
View File
@@ -5,6 +5,15 @@ declare var global: any;
const jsdom = new JSDOM("<!doctype html><html><body></body></html>");
const { window } = jsdom;
// tiny shim for getSelection for the RTE.
// tslint:disable:no-empty
window.getSelection = () =>
({
addRange() {},
removeAllRanges() {},
} as any);
// tslint:enable:no-empty
function copyProps(src: any, target: any) {
const props = Object.getOwnPropertyNames(src)
.filter(prop => typeof target[prop] === "undefined")