From c66c5a1adea87ca749f6f79d116126a27d2b223f Mon Sep 17 00:00:00 2001 From: Chi Vinh Le Date: Wed, 29 Aug 2018 20:37:18 +0200 Subject: [PATCH 01/18] Implement post comment mutation --- src/core/client/framework/helpers/getMe.ts | 16 +++++ src/core/client/framework/helpers/index.ts | 1 + .../stream/mutations/CreateCommentMutation.ts | 68 ++++++++++++++----- .../server/graph/tenant/resolvers/mutation.ts | 14 ++-- .../server/graph/tenant/schema/schema.graphql | 4 +- 5 files changed, 79 insertions(+), 24 deletions(-) create mode 100644 src/core/client/framework/helpers/getMe.ts create mode 100644 src/core/client/framework/helpers/index.ts diff --git a/src/core/client/framework/helpers/getMe.ts b/src/core/client/framework/helpers/getMe.ts new file mode 100644 index 000000000..a8ecbdc7a --- /dev/null +++ b/src/core/client/framework/helpers/getMe.ts @@ -0,0 +1,16 @@ +declare module "relay-runtime" { + export const ROOT_ID: string; +} + +import { Environment, ROOT_ID } from "relay-runtime"; + +export default function getMe(environment: Environment) { + const source = environment.getStore().getSource(); + const root = source.get(ROOT_ID)!; + const meKey = Object.keys(root).find(s => s.startsWith("me("))!; + if (!root[meKey]) { + return null; + } + const meID = root[meKey].__ref; + return source.get(meID)!; +} diff --git a/src/core/client/framework/helpers/index.ts b/src/core/client/framework/helpers/index.ts new file mode 100644 index 000000000..653f66d98 --- /dev/null +++ b/src/core/client/framework/helpers/index.ts @@ -0,0 +1 @@ +export { default as getMe } from "./getMe"; diff --git a/src/core/client/stream/mutations/CreateCommentMutation.ts b/src/core/client/stream/mutations/CreateCommentMutation.ts index a6ac895e4..0c25e2ce0 100644 --- a/src/core/client/stream/mutations/CreateCommentMutation.ts +++ b/src/core/client/stream/mutations/CreateCommentMutation.ts @@ -1,11 +1,14 @@ import { graphql } from "react-relay"; import { Environment } from "relay-runtime"; +import uuid from "uuid/v4"; +import { getMe } from "talk-framework/helpers"; import { commitMutationPromiseNormalized, createMutationContainer, } from "talk-framework/lib/relay"; import { Omit } from "talk-framework/types"; + import { CreateCommentMutationResponse, CreateCommentMutationVariables, @@ -19,12 +22,13 @@ export type CreateCommentInput = Omit< const mutation = graphql` mutation CreateCommentMutation($input: CreateCommentInput!) { createComment(input: $input) { - comment { - id - author { - username + commentEdge { + cursor + node { + id + ...CommentContainer + ...ReplyListContainer_comment } - body } clientMutationId } @@ -34,6 +38,8 @@ const mutation = graphql` let clientMutationId = 0; function commit(environment: Environment, input: CreateCommentInput) { + const me = getMe(environment)!; + const currentDate = new Date().toISOString(); return commitMutationPromiseNormalized< CreateCommentMutationResponse["createComment"], CreateCommentMutationVariables @@ -42,22 +48,48 @@ function commit(environment: Environment, input: CreateCommentInput) { variables: { input: { ...input, + clientMutationId: clientMutationId.toString(), + }, + }, + optimisticResponse: { + createComment: { + commentEdge: { + cursor: currentDate, + node: { + id: uuid(), + createdAt: currentDate, + author: { + id: me.id, + username: me.username, + }, + body: input.body, + replies: { + edges: [], + pageInfo: { + endCursor: null, + hasNextPage: false, + }, + }, + __typename: "Comment", + }, + }, clientMutationId: (clientMutationId++).toString(), }, }, - updater: store => { - const payload = store.getRootField("createComment"); - if (payload) { - const newRecord = payload.getLinkedRecord("comment")!; - const root = store.getRoot(); - const records = root.getLinkedRecords("comments"); - if (!records) { - throw new Error("Unexpected cache state"); - } - - root.setLinkedRecords([...records, newRecord], "comments"); - } - }, + configs: [ + { + type: "RANGE_ADD", + connectionInfo: [ + { + key: "Stream_comments", + rangeBehavior: "prepend", + filters: { orderBy: "CREATED_AT_DESC" }, + }, + ], + parentID: input.assetID, + edgeName: "commentEdge", + }, + ], }); } diff --git a/src/core/server/graph/tenant/resolvers/mutation.ts b/src/core/server/graph/tenant/resolvers/mutation.ts index 8f15117c9..002a3e3b9 100644 --- a/src/core/server/graph/tenant/resolvers/mutation.ts +++ b/src/core/server/graph/tenant/resolvers/mutation.ts @@ -1,10 +1,16 @@ import { GQLMutationTypeResolver } from "talk-server/graph/tenant/schema/__generated__/types"; const Mutation: GQLMutationTypeResolver = { - createComment: async (source, { input }, ctx) => ({ - comment: await ctx.mutators.Comment.create(input), - clientMutationId: input.clientMutationId, - }), + createComment: async (source, { input }, ctx) => { + const comment = await ctx.mutators.Comment.create(input); + return { + commentEdge: { + cursor: comment.created_at, + node: comment, + }, + clientMutationId: input.clientMutationId, + }; + }, updateSettings: async (source, { input }, ctx) => ({ settings: await ctx.mutators.Settings.update(input.settings), clientMutationId: input.clientMutationId, diff --git a/src/core/server/graph/tenant/schema/schema.graphql b/src/core/server/graph/tenant/schema/schema.graphql index d51f4be16..e0684ae65 100644 --- a/src/core/server/graph/tenant/schema/schema.graphql +++ b/src/core/server/graph/tenant/schema/schema.graphql @@ -821,9 +821,9 @@ mutation. """ type CreateCommentPayload { """ - comment is the possibly created comment. + CommentEdge is the possibly created comment edge. """ - comment: Comment + commentEdge: CommentEdge """ clientMutationId is required for Relay support. From 0497718764abd15f3358cf6b11b572d54b566bae Mon Sep 17 00:00:00 2001 From: Chi Vinh Le Date: Thu, 30 Aug 2018 10:21:54 +0200 Subject: [PATCH 02/18] WIP --- package-lock.json | 130 ++++++++++++------ package.json | 11 +- .../client/framework/testHelpers/index.ts | 5 +- .../testHelpers/removeFragmentRefs.ts | 16 +++ .../containers/CommentContainer.spec.tsx | 12 +- .../containers/ReplyListContainer.spec.tsx | 16 ++- .../stream/containers/ReplyListContainer.tsx | 2 +- .../containers/StreamContainer.spec.tsx | 12 +- .../containers/UserBoxContainer.spec.tsx | 8 +- 9 files changed, 146 insertions(+), 66 deletions(-) create mode 100644 src/core/client/framework/testHelpers/removeFragmentRefs.ts diff --git a/package-lock.json b/package-lock.json index a62e7d284..7130b63f3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3697,13 +3697,14 @@ } }, "babel-plugin-relay": { - "version": "github:coralproject/patched#80c179a21ece1a43e4782c776f3041fe5ab14a4a", - "from": "github:coralproject/patched#babel-plugin-relay", + "version": "1.7.0-rc.1", + "resolved": "https://registry.npmjs.org/babel-plugin-relay/-/babel-plugin-relay-1.7.0-rc.1.tgz", + "integrity": "sha512-VklvYrB0kSZeDHGtwJNtDtoQNVrI+ZclURnsCBwWU5oIX4f21//cYeQ9oooJDIvXcCApFKhaozP5tfWgk+Fo5g==", "dev": true, "requires": { + "babel-plugin-macros": "^2.0.0", "babel-runtime": "^6.23.0", - "babel-types": "^6.24.1", - "graphql": "^0.13.0" + "babel-types": "^6.24.1" } }, "babel-plugin-syntax-class-properties": { @@ -4043,9 +4044,9 @@ } }, "babel-preset-fbjs": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/babel-preset-fbjs/-/babel-preset-fbjs-2.1.4.tgz", - "integrity": "sha512-6XVQwlO26V5/0P9s2Eje8Epqkv/ihaMJ798+W98ktOA8fCn2IFM6wEi7CDW3fTbKFZ/8fDGvGZH01B6GSuNiWA==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/babel-preset-fbjs/-/babel-preset-fbjs-2.2.0.tgz", + "integrity": "sha512-jj0KFJDioYZMtPtZf77dQuU+Ad/1BtN0UnAYlHDa8J8f4tGXr3YrPoJImD5MdueaOPeN/jUdrCgu330EfXr0XQ==", "dev": true, "requires": { "babel-plugin-check-es2015-constants": "^6.8.0", @@ -5157,12 +5158,6 @@ } } }, - "babylon": { - "version": "7.0.0-beta.31", - "resolved": "https://registry.npmjs.org/babylon/-/babylon-7.0.0-beta.31.tgz", - "integrity": "sha512-6lm2mV3S51yEnKmQQNnswoABL1U1H1KHoCCVwdwI3hvIv+W7ya4ki7Aw4o4KxtUHjNKkK5WpZb22rrMMOcJXJQ==", - "dev": true - }, "bach": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/bach/-/bach-1.2.0.tgz", @@ -11095,6 +11090,32 @@ "iterall": "^1.2.1" } }, + "graphql-compiler": { + "version": "1.7.0-rc.1", + "resolved": "https://registry.npmjs.org/graphql-compiler/-/graphql-compiler-1.7.0-rc.1.tgz", + "integrity": "sha512-wu1HMcI39I2Ion1N+nrn9ELGMFGi6lFDz+dJuJwnlO3BdXTrSVvYnnusxNaim4/Gkmf9tM/ErYSbpvRzBWPk3g==", + "dev": true, + "requires": { + "chalk": "^1.1.1", + "fb-watchman": "^2.0.0", + "immutable": "~3.7.6" + }, + "dependencies": { + "chalk": { + "version": "1.1.3", + "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "requires": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + } + } + } + }, "graphql-config": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/graphql-config/-/graphql-config-2.0.1.tgz", @@ -20889,13 +20910,15 @@ } }, "react-relay": { - "version": "github:coralproject/patched#aad037f8cbcd9c14d5f6fd96cfafb24c39eab937", - "from": "github:coralproject/patched#react-relay", + "version": "1.7.0-rc.1", + "resolved": "https://registry.npmjs.org/react-relay/-/react-relay-1.7.0-rc.1.tgz", + "integrity": "sha512-5BcnFr++zkfq5AVoCjcqr8eotuJwVN+57+BnhJZuw3EoY8Yi+avV/AqZ7Tz9F6r9p5xZkCoYZw3kRTtpK9lPSQ==", "dev": true, "requires": { "babel-runtime": "^6.23.0", - "fbjs": "^0.8.14", - "prop-types": "^15.5.8" + "fbjs": "0.8.17", + "prop-types": "^15.5.8", + "relay-runtime": "1.7.0-rc.1" } }, "react-responsive": { @@ -21332,27 +21355,59 @@ "dev": true }, "relay-compiler": { - "version": "github:coralproject/patched#652bdfa85cf35ed3e8dc4e26cf26e9d1744873cb", - "from": "github:coralproject/patched#relay-compiler", + "version": "1.7.0-rc.1", + "resolved": "https://registry.npmjs.org/relay-compiler/-/relay-compiler-1.7.0-rc.1.tgz", + "integrity": "sha512-NCbrAWG692dp0on3sefX0pZ+1/eWDPOnme/i0vAbH38ejfRW5vPS9pznHYS7A0y7OC5lABMZT+DCyEYeHFpflA==", "dev": true, "requires": { - "babel-generator": "^6.26.0", + "@babel/generator": "7.0.0-beta.56", + "@babel/parser": "7.0.0-beta.56", + "@babel/types": "7.0.0-beta.56", "babel-polyfill": "^6.20.0", - "babel-preset-fbjs": "^2.1.4", + "babel-preset-fbjs": "2.2.0", "babel-runtime": "^6.23.0", "babel-traverse": "^6.26.0", - "babel-types": "^6.24.1", - "babylon": "^7.0.0-beta", "chalk": "^1.1.1", - "fast-glob": "^2.0.0", + "fast-glob": "^2.2.2", "fb-watchman": "^2.0.0", - "fbjs": "^0.8.14", - "graphql": "^0.13.0", + "fbjs": "0.8.17", + "graphql-compiler": "1.7.0-rc.1", "immutable": "~3.7.6", + "relay-runtime": "1.7.0-rc.1", "signedsource": "^1.0.0", "yargs": "^9.0.0" }, "dependencies": { + "@babel/generator": { + "version": "7.0.0-beta.56", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.0.0-beta.56.tgz", + "integrity": "sha512-d+Ls/Vr5OU5FBDYQToXSqAluI3r2UaSoNZ41zD3sxdoVoaT8K5Bdh4So4eG4o//INGM7actValXGfb+5J1+r8w==", + "dev": true, + "requires": { + "@babel/types": "7.0.0-beta.56", + "jsesc": "^2.5.1", + "lodash": "^4.17.10", + "source-map": "^0.5.0", + "trim-right": "^1.0.1" + } + }, + "@babel/parser": { + "version": "7.0.0-beta.56", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.0.0-beta.56.tgz", + "integrity": "sha512-JM0ughhbo+sPXw2Z+SUyowfYrAOhjanzjMshcLswBdXVelJCOeEKe/FqMqPWGVPQr7wByongXIn+MKdCpY7DBw==", + "dev": true + }, + "@babel/types": { + "version": "7.0.0-beta.56", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.0.0-beta.56.tgz", + "integrity": "sha512-fRIBeHtKxAD3D1E7hYSpG4MnLt0AfzHHs5gfVclOB0NlfLu3qiWU/IqdbK2ixTK61424iEkV1P/VAzndx6ungA==", + "dev": true, + "requires": { + "esutils": "^2.0.2", + "lodash": "^4.17.10", + "to-fast-properties": "^2.0.0" + } + }, "babel-polyfill": { "version": "6.26.0", "resolved": "https://registry.npmjs.org/babel-polyfill/-/babel-polyfill-6.26.0.tgz", @@ -21535,8 +21590,9 @@ } }, "relay-compiler-language-typescript": { - "version": "github:coralproject/patched#0da4835cf006e905c154d1e41aa978f81ace54f3", - "from": "github:coralproject/patched#relay-compiler-language-typescript", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/relay-compiler-language-typescript/-/relay-compiler-language-typescript-1.1.0.tgz", + "integrity": "sha512-HDWTEsSsNuphmNrjbc1Tf1ljVjbi2CSA7zArsFFYCYHfVU/O7oS3qv/u5E+LrHdYSM80AjpUVjdcFGsqkgOmNw==", "dev": true, "requires": { "immutable": "^3.7.6", @@ -21550,23 +21606,13 @@ "dev": true }, "relay-runtime": { - "version": "github:coralproject/patched#9dda7718393cb3aa6647bd3badf574c025f79d12", - "from": "github:coralproject/patched#relay-runtime", + "version": "1.7.0-rc.1", + "resolved": "https://registry.npmjs.org/relay-runtime/-/relay-runtime-1.7.0-rc.1.tgz", + "integrity": "sha512-gdhtIKojPA0FJPc4hPGAbxZBAR2xpnCvCuoL4/wu3ffHxwUFvSSlBIptW3GSxTRM2F55vl7I1IeehCS9IAfQPA==", "dev": true, "requires": { "babel-runtime": "^6.23.0", - "fbjs": "^0.8.14" - } - }, - "relay-test-utils": { - "version": "github:coralproject/patched#eee4232d3996db88c4ae9030023adcf1f3674615", - "from": "github:coralproject/patched#relay-test-utils", - "dev": true, - "requires": { - "babel-runtime": "^6.23.0", - "fbjs": "^0.8.14", - "graphql": "^0.13.0", - "prop-types": "^15.5.8" + "fbjs": "0.8.17" } }, "remark": { diff --git a/package.json b/package.json index fe98d87a8..f4921415f 100644 --- a/package.json +++ b/package.json @@ -134,7 +134,7 @@ "babel-core": "^7.0.0-bridge.0", "babel-loader": "^8.0.0-beta", "babel-plugin-module-resolver": "^3.1.1", - "babel-plugin-relay": "github:coralproject/patched#babel-plugin-relay", + "babel-plugin-relay": "^1.7.0-rc.1", "babel-preset-react-optimize": "^1.0.1", "case-sensitive-paths-webpack-plugin": "^2.1.2", "chalk": "^2.4.1", @@ -194,17 +194,16 @@ "react-dom": "^16.4.0", "react-final-form": "^3.6.4", "react-popper": "^1.0.0", - "react-relay": "github:coralproject/patched#react-relay", + "react-relay": "^1.7.0-rc.1", "react-responsive": "^5.0.0", "react-test-renderer": "^16.4.2", "react-timeago": "^4.1.9", "react-with-state-props": "^2.0.4", "recompose": "^0.27.1", - "relay-compiler": "github:coralproject/patched#relay-compiler", - "relay-compiler-language-typescript": "github:coralproject/patched#relay-compiler-language-typescript", + "relay-compiler": "^1.7.0-rc.1", + "relay-compiler-language-typescript": "^1.1.0", "relay-local-schema": "^0.7.0", - "relay-runtime": "github:coralproject/patched#relay-runtime", - "relay-test-utils": "github:coralproject/patched#relay-test-utils", + "relay-runtime": "^1.7.0-rc.1", "sane": "^2.5.2", "simulant": "^0.2.2", "sinon": "^6.1.3", diff --git a/src/core/client/framework/testHelpers/index.ts b/src/core/client/framework/testHelpers/index.ts index 182ba19fb..484fb1d65 100644 --- a/src/core/client/framework/testHelpers/index.ts +++ b/src/core/client/framework/testHelpers/index.ts @@ -2,5 +2,8 @@ export { default as createRelayEnvironment, CreateRelayEnvironmentParams, } from "./createRelayEnvironment"; - export { default as createFluentBundle } from "./createFluentBundle"; +export { + default as removeFragmentRefs, + NoFragmentRefs, +} from "./removeFragmentRefs"; diff --git a/src/core/client/framework/testHelpers/removeFragmentRefs.ts b/src/core/client/framework/testHelpers/removeFragmentRefs.ts new file mode 100644 index 000000000..b92e57aa9 --- /dev/null +++ b/src/core/client/framework/testHelpers/removeFragmentRefs.ts @@ -0,0 +1,16 @@ +import { ComponentType } from "react"; + +/** Remove all traces of `$fragmentRefs` and `$refType` from type recursively */ +export type NoFragmentRefs = T extends object + ? { + [P in Exclude]: NoFragmentRefs< + T[P] + > + } + : T; + +export default function removeFragmentRefs( + component: ComponentType +): ComponentType> { + return component as any; +} diff --git a/src/core/client/stream/containers/CommentContainer.spec.tsx b/src/core/client/stream/containers/CommentContainer.spec.tsx index 44e4a0a19..892957994 100644 --- a/src/core/client/stream/containers/CommentContainer.spec.tsx +++ b/src/core/client/stream/containers/CommentContainer.spec.tsx @@ -1,12 +1,16 @@ import { shallow } from "enzyme"; import React from "react"; +import { removeFragmentRefs } from "talk-framework/testHelpers"; import { PropTypesOf } from "talk-framework/types"; import { CommentContainer } from "./CommentContainer"; +// Remove relay refs so we can stub the props. +const CommentContainerN = removeFragmentRefs(CommentContainer); + it("renders username and body", () => { - const props: PropTypesOf = { + const props: PropTypesOf = { data: { id: "comment-id", author: { @@ -17,12 +21,12 @@ it("renders username and body", () => { }, }; - const wrapper = shallow(); + const wrapper = shallow(); expect(wrapper).toMatchSnapshot(); }); it("renders body only", () => { - const props: PropTypesOf = { + const props: PropTypesOf = { data: { id: "comment-id", author: { @@ -33,6 +37,6 @@ it("renders body only", () => { }, }; - const wrapper = shallow(); + const wrapper = shallow(); expect(wrapper).toMatchSnapshot(); }); diff --git a/src/core/client/stream/containers/ReplyListContainer.spec.tsx b/src/core/client/stream/containers/ReplyListContainer.spec.tsx index 3a414dd51..9096f3614 100644 --- a/src/core/client/stream/containers/ReplyListContainer.spec.tsx +++ b/src/core/client/stream/containers/ReplyListContainer.spec.tsx @@ -2,13 +2,17 @@ import { shallow, ShallowWrapper } from "enzyme"; import { noop } from "lodash"; import React from "react"; +import { removeFragmentRefs } from "talk-framework/testHelpers"; import { PropTypesOf } from "talk-framework/types"; import ReplyList from "../components/ReplyList"; import { ReplyListContainer } from "./ReplyListContainer"; +// Remove relay refs so we can stub the props. +const ReplyListContainerN = removeFragmentRefs(ReplyListContainer); + it("renders correctly", () => { - const props: PropTypesOf = { + const props: PropTypesOf = { comment: { id: "comment-id", replies: { @@ -20,12 +24,12 @@ it("renders correctly", () => { isLoading: noop, } as any, }; - const wrapper = shallow(); + const wrapper = shallow(); expect(wrapper).toMatchSnapshot(); }); it("renders correctly when replies are null", () => { - const props: PropTypesOf = { + const props: PropTypesOf = { comment: { id: "comment-id", replies: null, @@ -35,13 +39,13 @@ it("renders correctly when replies are null", () => { isLoading: noop, } as any, }; - const wrapper = shallow(); + const wrapper = shallow(); expect(wrapper).toMatchSnapshot(); }); describe("when has more replies", () => { let finishLoading: ((error?: Error) => void) | null = null; - const props: PropTypesOf = { + const props: PropTypesOf = { comment: { id: "comment-id", replies: { @@ -57,7 +61,7 @@ describe("when has more replies", () => { let wrapper: ShallowWrapper; - beforeAll(() => (wrapper = shallow())); + beforeAll(() => (wrapper = shallow())); it("renders hasMore", () => { expect(wrapper).toMatchSnapshot(); diff --git a/src/core/client/stream/containers/ReplyListContainer.tsx b/src/core/client/stream/containers/ReplyListContainer.tsx index 48db9645a..447090289 100644 --- a/src/core/client/stream/containers/ReplyListContainer.tsx +++ b/src/core/client/stream/containers/ReplyListContainer.tsx @@ -23,7 +23,7 @@ export class ReplyListContainer extends React.Component { public render() { if ( - this.props.comment.replies === null || + !this.props.comment.replies == null || this.props.comment.replies.edges.length === 0 ) { return null; diff --git a/src/core/client/stream/containers/StreamContainer.spec.tsx b/src/core/client/stream/containers/StreamContainer.spec.tsx index 2e19bda86..411bf518a 100644 --- a/src/core/client/stream/containers/StreamContainer.spec.tsx +++ b/src/core/client/stream/containers/StreamContainer.spec.tsx @@ -2,13 +2,17 @@ import { shallow, ShallowWrapper } from "enzyme"; import { noop } from "lodash"; import React from "react"; +import { removeFragmentRefs } from "talk-framework/testHelpers"; import { PropTypesOf } from "talk-framework/types"; import Stream from "../components/Stream"; import { StreamContainer } from "./StreamContainer"; +// Remove relay refs so we can stub the props. +const StreamContainerN = removeFragmentRefs(StreamContainer); + it("renders correctly", () => { - const props: PropTypesOf = { + const props: PropTypesOf = { asset: { id: "asset-id", isClosed: false, @@ -21,13 +25,13 @@ it("renders correctly", () => { isLoading: noop, } as any, }; - const wrapper = shallow(); + const wrapper = shallow(); expect(wrapper).toMatchSnapshot(); }); describe("when has more comments", () => { let finishLoading: ((error?: Error) => void) | null = null; - const props: PropTypesOf = { + const props: PropTypesOf = { asset: { id: "asset-id", isClosed: false, @@ -44,7 +48,7 @@ describe("when has more comments", () => { let wrapper: ShallowWrapper; - beforeAll(() => (wrapper = shallow())); + beforeAll(() => (wrapper = shallow())); it("renders hasMore", () => { expect(wrapper).toMatchSnapshot(); diff --git a/src/core/client/stream/containers/UserBoxContainer.spec.tsx b/src/core/client/stream/containers/UserBoxContainer.spec.tsx index d40d0878a..f99832b8e 100644 --- a/src/core/client/stream/containers/UserBoxContainer.spec.tsx +++ b/src/core/client/stream/containers/UserBoxContainer.spec.tsx @@ -1,12 +1,16 @@ import { shallow } from "enzyme"; import React from "react"; +import { removeFragmentRefs } from "talk-framework/testHelpers"; import { PropTypesOf } from "talk-framework/types"; import { UserBoxContainer } from "./UserBoxContainer"; +// Remove relay refs so we can stub the props. +const UserBoxContainerN = removeFragmentRefs(UserBoxContainer); + it("renders correctly", () => { - const props: PropTypesOf = { + const props: PropTypesOf = { local: { authPopup: { open: false, @@ -22,6 +26,6 @@ it("renders correctly", () => { // tslint:disable-next-line:no-empty signOut: async () => {}, }; - const wrapper = shallow(); + const wrapper = shallow(); expect(wrapper).toMatchSnapshot(); }); From 7ab70b8484abc332585b06f8e5f39cc46f10d7f8 Mon Sep 17 00:00:00 2001 From: Chi Vinh Le Date: Thu, 30 Aug 2018 15:41:08 +0200 Subject: [PATCH 03/18] Update relay-runtime types --- package-lock.json | 8 ++--- package.json | 2 +- src/core/client/framework/helpers/getMe.ts | 4 --- .../lib/relay/commitMutationPromise.ts | 18 +++++------ src/core/client/stream/components/App.tsx | 2 +- .../PermalinkButton/PermalinkButton.tsx | 1 + .../containers/PostCommentFormContainer.tsx | 3 +- .../stream/containers/ReplyListContainer.tsx | 2 +- .../containers/StreamContainer.spec.tsx | 2 ++ .../stream/mutations/CreateCommentMutation.ts | 30 +++++++------------ .../server/graph/tenant/schema/schema.graphql | 2 +- 11 files changed, 32 insertions(+), 42 deletions(-) diff --git a/package-lock.json b/package-lock.json index 7130b63f3..11b85ab82 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1342,9 +1342,9 @@ } }, "@coralproject/rte": { - "version": "0.10.6", - "resolved": "https://registry.npmjs.org/@coralproject/rte/-/rte-0.10.6.tgz", - "integrity": "sha512-oUK/KSw28AkmpX5D8v1IkQT0iIxKxPmSG0euTFgqEthSgnAUQgoOmQengaR/nsS9zE31KPbbWMIAzH0aN/YwNA==", + "version": "0.10.9", + "resolved": "https://registry.npmjs.org/@coralproject/rte/-/rte-0.10.9.tgz", + "integrity": "sha512-0K+bc3JaOhjgJJ91uupev4EmSoldd/IizfjRgAQ8LbZjRc3pWprzIBhoTMChApBzz7eI9DUfw2ULg5muziV8BA==", "dev": true, "requires": { "bowser": "^1.0.0", @@ -2107,7 +2107,7 @@ "dev": true }, "@types/relay-runtime": { - "version": "github:coralproject/patched#ba8d413696e97b4f67450de3525cc319b9980cba", + "version": "github:coralproject/patched#c9a2387f6d32f092b9c75ddfca92d1856d2b41f6", "from": "github:coralproject/patched#types/relay-runtime", "dev": true }, diff --git a/package.json b/package.json index f4921415f..6b41d9457 100644 --- a/package.json +++ b/package.json @@ -76,7 +76,7 @@ "@babel/polyfill": "7.0.0-beta.49", "@babel/preset-env": "7.0.0-beta.49", "@babel/preset-react": "7.0.0-beta.49", - "@coralproject/rte": "^0.10.6", + "@coralproject/rte": "^0.10.9", "@types/bcryptjs": "^2.4.1", "@types/bunyan": "^1.8.4", "@types/case-sensitive-paths-webpack-plugin": "^2.1.2", diff --git a/src/core/client/framework/helpers/getMe.ts b/src/core/client/framework/helpers/getMe.ts index a8ecbdc7a..07eeb5952 100644 --- a/src/core/client/framework/helpers/getMe.ts +++ b/src/core/client/framework/helpers/getMe.ts @@ -1,7 +1,3 @@ -declare module "relay-runtime" { - export const ROOT_ID: string; -} - import { Environment, ROOT_ID } from "relay-runtime"; export default function getMe(environment: Environment) { diff --git a/src/core/client/framework/lib/relay/commitMutationPromise.ts b/src/core/client/framework/lib/relay/commitMutationPromise.ts index 97fb23088..b94fd7dde 100644 --- a/src/core/client/framework/lib/relay/commitMutationPromise.ts +++ b/src/core/client/framework/lib/relay/commitMutationPromise.ts @@ -1,5 +1,5 @@ import { commitMutation } from "react-relay"; -import { Environment, MutationConfig } from "relay-runtime"; +import { Environment, MutationConfig, OperationBase } from "relay-runtime"; import { Omit } from "talk-framework/types"; @@ -7,8 +7,8 @@ import { Omit } from "talk-framework/types"; * Like `MutationConfig` but omits `onCompleted` and `onError` * because we are going to use a Promise API. */ -export type MutationPromiseConfig = Omit< - MutationConfig, +export type MutationPromiseConfig = Omit< + MutationConfig, "onCompleted" | "onError" >; @@ -27,10 +27,10 @@ function getPayload(response: { [key: string]: any }): any { * and errors are wrapped inside of application specific * error instances. */ -export async function commitMutationPromiseNormalized( +export async function commitMutationPromiseNormalized( environment: Environment, - config: MutationPromiseConfig -): Promise { + config: MutationPromiseConfig +): Promise { try { const response = await commitMutationPromise(environment, config); return getPayload(response); @@ -42,10 +42,10 @@ export async function commitMutationPromiseNormalized( /** * Like `commitMutation` of the Relay API but returns a Promise. */ -export function commitMutationPromise( +export function commitMutationPromise( environment: Environment, - config: MutationPromiseConfig -): Promise { + config: MutationPromiseConfig +): Promise { return new Promise((resolve, reject) => { commitMutation(environment, { ...config, diff --git a/src/core/client/stream/components/App.tsx b/src/core/client/stream/components/App.tsx index a09c49149..b7d2d09d9 100644 --- a/src/core/client/stream/components/App.tsx +++ b/src/core/client/stream/components/App.tsx @@ -2,9 +2,9 @@ import * as React from "react"; import { StatelessComponent } from "react"; import { Flex } from "talk-ui/components"; + import PermalinkViewQuery from "../queries/PermalinkViewQuery"; import StreamQuery from "../queries/StreamQuery"; - import * as styles from "./App.css"; export interface AppProps { diff --git a/src/core/client/stream/components/PermalinkButton/PermalinkButton.tsx b/src/core/client/stream/components/PermalinkButton/PermalinkButton.tsx index 12540c1d3..cf0fad56c 100644 --- a/src/core/client/stream/components/PermalinkButton/PermalinkButton.tsx +++ b/src/core/client/stream/components/PermalinkButton/PermalinkButton.tsx @@ -1,5 +1,6 @@ import { Localized } from "fluent-react/compat"; import React from "react"; + import { oncePerFrame } from "talk-common/utils"; import { Button, diff --git a/src/core/client/stream/containers/PostCommentFormContainer.tsx b/src/core/client/stream/containers/PostCommentFormContainer.tsx index bf4ab8cf9..6398025eb 100644 --- a/src/core/client/stream/containers/PostCommentFormContainer.tsx +++ b/src/core/client/stream/containers/PostCommentFormContainer.tsx @@ -1,4 +1,4 @@ -import React, { Component } from "react"; +import React, { Component, ReactNode } from "react"; import { BadUserInputError } from "talk-framework/lib/errors"; import { PropTypesOf } from "talk-framework/types"; @@ -11,6 +11,7 @@ import { CreateCommentMutation, withCreateCommentMutation } from "../mutations"; interface InnerProps { createComment: CreateCommentMutation; assetID: string; + children?: ReactNode; } class PostCommentFormContainer extends Component { diff --git a/src/core/client/stream/containers/ReplyListContainer.tsx b/src/core/client/stream/containers/ReplyListContainer.tsx index 447090289..4ee7165e6 100644 --- a/src/core/client/stream/containers/ReplyListContainer.tsx +++ b/src/core/client/stream/containers/ReplyListContainer.tsx @@ -23,7 +23,7 @@ export class ReplyListContainer extends React.Component { public render() { if ( - !this.props.comment.replies == null || + this.props.comment.replies == null || this.props.comment.replies.edges.length === 0 ) { return null; diff --git a/src/core/client/stream/containers/StreamContainer.spec.tsx b/src/core/client/stream/containers/StreamContainer.spec.tsx index 411bf518a..531c74970 100644 --- a/src/core/client/stream/containers/StreamContainer.spec.tsx +++ b/src/core/client/stream/containers/StreamContainer.spec.tsx @@ -20,6 +20,7 @@ it("renders correctly", () => { edges: [{ node: { id: "comment-1" } }, { node: { id: "comment-2" } }], }, }, + user: null, relay: { hasMore: noop, isLoading: noop, @@ -39,6 +40,7 @@ describe("when has more comments", () => { edges: [{ node: { id: "comment-1" } }, { node: { id: "comment-2" } }], }, }, + user: null, relay: { hasMore: () => true, isLoading: () => false, diff --git a/src/core/client/stream/mutations/CreateCommentMutation.ts b/src/core/client/stream/mutations/CreateCommentMutation.ts index 0c25e2ce0..0482fd238 100644 --- a/src/core/client/stream/mutations/CreateCommentMutation.ts +++ b/src/core/client/stream/mutations/CreateCommentMutation.ts @@ -9,13 +9,10 @@ import { } from "talk-framework/lib/relay"; import { Omit } from "talk-framework/types"; -import { - CreateCommentMutationResponse, - CreateCommentMutationVariables, -} from "talk-stream/__generated__/CreateCommentMutation.graphql"; +import { CreateCommentMutation } from "talk-stream/__generated__/CreateCommentMutation.graphql"; export type CreateCommentInput = Omit< - CreateCommentMutationVariables["input"], + CreateCommentMutation["variables"]["input"], "clientMutationId" >; @@ -26,8 +23,12 @@ const mutation = graphql` cursor node { id - ...CommentContainer - ...ReplyListContainer_comment + author { + id + username + } + body + createdAt } } clientMutationId @@ -40,10 +41,7 @@ let clientMutationId = 0; function commit(environment: Environment, input: CreateCommentInput) { const me = getMe(environment)!; const currentDate = new Date().toISOString(); - return commitMutationPromiseNormalized< - CreateCommentMutationResponse["createComment"], - CreateCommentMutationVariables - >(environment, { + return commitMutationPromiseNormalized(environment, { mutation, variables: { input: { @@ -63,14 +61,6 @@ function commit(environment: Environment, input: CreateCommentInput) { username: me.username, }, body: input.body, - replies: { - edges: [], - pageInfo: { - endCursor: null, - hasNextPage: false, - }, - }, - __typename: "Comment", }, }, clientMutationId: (clientMutationId++).toString(), @@ -100,4 +90,4 @@ export const withCreateCommentMutation = createMutationContainer( export type CreateCommentMutation = ( input: CreateCommentInput -) => Promise; +) => Promise; diff --git a/src/core/server/graph/tenant/schema/schema.graphql b/src/core/server/graph/tenant/schema/schema.graphql index e0684ae65..d32d6259f 100644 --- a/src/core/server/graph/tenant/schema/schema.graphql +++ b/src/core/server/graph/tenant/schema/schema.graphql @@ -577,7 +577,7 @@ type Comment { """ createdAt is the date in which the comment was created. """ - createdAt: Time + createdAt: Time! """ author is the User that authored the Comment. From e2bcc4631cbfb65eaf8e144742d3de1124e70d9f Mon Sep 17 00:00:00 2001 From: Chi Vinh Le Date: Thu, 30 Aug 2018 15:51:06 +0200 Subject: [PATCH 04/18] Fix remaining type issues --- package-lock.json | 245 ++++++++++++++++++++++++++++------------------ package.json | 1 + 2 files changed, 152 insertions(+), 94 deletions(-) diff --git a/package-lock.json b/package-lock.json index 11b85ab82..452c8c4b8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2188,6 +2188,15 @@ "@types/node": "*" } }, + "@types/vinyl": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@types/vinyl/-/vinyl-2.0.2.tgz", + "integrity": "sha512-2iYpNuOl98SrLPBZfEN9Mh2JCJ2EI9HU35SfgBEb51DcmaHkhp8cKMblYeBqMQiwXMgAD3W60DbQ4i/UdLiXhw==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, "@types/webpack": { "version": "4.4.7", "resolved": "https://registry.npmjs.org/@types/webpack/-/webpack-4.4.7.tgz", @@ -8425,6 +8434,12 @@ "long": "^3.2.0" } }, + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "dev": true + }, "babylon": { "version": "6.18.0", "resolved": "https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz", @@ -8485,14 +8500,6 @@ "dev": true, "requires": { "ms": "2.0.0" - }, - "dependencies": { - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - } } } } @@ -8543,6 +8550,21 @@ "path-exists": "^3.0.0" } }, + "minimatch": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.3.tgz", + "integrity": "sha1-Kk5AkLlrLbBqnX3wEFWmKnfJt3Q=", + "dev": true, + "requires": { + "brace-expansion": "^1.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, "p-limit": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.0.0.tgz", @@ -8568,9 +8590,9 @@ "dev": true }, "react-dev-utils": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-5.0.1.tgz", - "integrity": "sha512-+y92rG6pmXt3cpcg/NGmG4w/W309tWNSmyyPL8hCMxuCSg2UP/hUg3npACj2UZc8UKVSXexyLrCnxowizGoAsw==", + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-5.0.2.tgz", + "integrity": "sha512-d2FbKvYe4XAQx5gjHBoWG+ADqC3fGZzjb7i9vxd/Y5xfLkBGtQyX7aOb8lBRQPYUhjngiD3d49LevjY1stUR0Q==", "dev": true, "requires": { "address": "1.0.3", @@ -8585,17 +8607,17 @@ "inquirer": "3.3.0", "is-root": "1.0.0", "opn": "5.2.0", - "react-error-overlay": "^4.0.0", + "react-error-overlay": "^4.0.1", "recursive-readdir": "2.2.1", "shell-quote": "1.6.1", - "sockjs-client": "1.1.4", + "sockjs-client": "1.1.5", "strip-ansi": "3.0.1", "text-table": "0.2.0" }, "dependencies": { "chalk": { "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", "dev": true, "requires": { @@ -8605,6 +8627,121 @@ "strip-ansi": "^3.0.0", "supports-color": "^2.0.0" } + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "filesize": { + "version": "3.5.11", + "resolved": "https://registry.npmjs.org/filesize/-/filesize-3.5.11.tgz", + "integrity": "sha512-ZH7loueKBoDb7yG9esn1U+fgq7BzlzW6NRi5/rMdxIZ05dj7GFD/Xc5rq2CDt5Yq86CyfSYVyx4242QQNZbx1g==", + "dev": true + }, + "gzip-size": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-3.0.0.tgz", + "integrity": "sha1-VGGI6b3DN/Zzdy+BZgRks4nc5SA=", + "dev": true, + "requires": { + "duplexer": "^0.1.1" + } + }, + "inquirer": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-3.3.0.tgz", + "integrity": "sha512-h+xtnyk4EwKvFWHrUYsWErEVR+igKtLdchu+o0Z1RL7VU/jVMFbYir2bp6bAj8efFNxWqHX0dIss6fJQ+/+qeQ==", + "dev": true, + "requires": { + "ansi-escapes": "^3.0.0", + "chalk": "^2.0.0", + "cli-cursor": "^2.1.0", + "cli-width": "^2.0.0", + "external-editor": "^2.0.4", + "figures": "^2.0.0", + "lodash": "^4.3.0", + "mute-stream": "0.0.7", + "run-async": "^2.2.0", + "rx-lite": "^4.0.8", + "rx-lite-aggregates": "^4.0.8", + "string-width": "^2.1.0", + "strip-ansi": "^4.0.0", + "through": "^2.3.6" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "chalk": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz", + "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dev": true, + "requires": { + "ansi-regex": "^3.0.0" + } + }, + "supports-color": { + "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" + } + } + } + }, + "react-error-overlay": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-4.0.1.tgz", + "integrity": "sha512-xXUbDAZkU08aAkjtUvldqbvI04ogv+a1XdHxvYuHPYKIVk/42BIOD0zSKTHAWV4+gDy3yGm283z2072rA2gdtw==", + "dev": true + }, + "recursive-readdir": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/recursive-readdir/-/recursive-readdir-2.2.1.tgz", + "integrity": "sha1-kO8jHQd4xc4JPJpI105cVCLROpk=", + "dev": true, + "requires": { + "minimatch": "3.0.3" + } + }, + "sockjs-client": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/sockjs-client/-/sockjs-client-1.1.5.tgz", + "integrity": "sha1-G7fA9yIsQPQq3xT0RCy9Eml3GoM=", + "dev": true, + "requires": { + "debug": "^2.6.6", + "eventsource": "0.1.6", + "faye-websocket": "~0.11.0", + "inherits": "^2.0.1", + "json3": "^3.3.2", + "url-parse": "^1.1.8" + } } } }, @@ -9787,12 +9924,6 @@ } } }, - "filesize": { - "version": "3.5.11", - "resolved": "https://registry.npmjs.org/filesize/-/filesize-3.5.11.tgz", - "integrity": "sha512-ZH7loueKBoDb7yG9esn1U+fgq7BzlzW6NRi5/rMdxIZ05dj7GFD/Xc5rq2CDt5Yq86CyfSYVyx4242QQNZbx1g==", - "dev": true - }, "fill-range": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", @@ -11539,15 +11670,6 @@ "glogg": "^1.0.0" } }, - "gzip-size": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-3.0.0.tgz", - "integrity": "sha1-VGGI6b3DN/Zzdy+BZgRks4nc5SA=", - "dev": true, - "requires": { - "duplexer": "^0.1.1" - } - }, "handle-thing": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-1.2.5.tgz", @@ -12229,45 +12351,6 @@ "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==", "dev": true }, - "inquirer": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-3.3.0.tgz", - "integrity": "sha512-h+xtnyk4EwKvFWHrUYsWErEVR+igKtLdchu+o0Z1RL7VU/jVMFbYir2bp6bAj8efFNxWqHX0dIss6fJQ+/+qeQ==", - "dev": true, - "requires": { - "ansi-escapes": "^3.0.0", - "chalk": "^2.0.0", - "cli-cursor": "^2.1.0", - "cli-width": "^2.0.0", - "external-editor": "^2.0.4", - "figures": "^2.0.0", - "lodash": "^4.3.0", - "mute-stream": "0.0.7", - "run-async": "^2.2.0", - "rx-lite": "^4.0.8", - "rx-lite-aggregates": "^4.0.8", - "string-width": "^2.1.0", - "strip-ansi": "^4.0.0", - "through": "^2.3.6" - }, - "dependencies": { - "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", - "dev": true - }, - "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "dev": true, - "requires": { - "ansi-regex": "^3.0.0" - } - } - } - }, "internal-ip": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/internal-ip/-/internal-ip-1.2.0.tgz", @@ -20801,12 +20884,6 @@ "create-emotion-styled": "^9.2.6" } }, - "react-error-overlay": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-4.0.0.tgz", - "integrity": "sha512-FlsPxavEyMuR6TjVbSSywovXSEyOg6ZDj5+Z8nbsRl9EkOzAhEIcS+GLoQDC5fz/t9suhUXWmUrOBrgeUvrMxw==", - "dev": true - }, "react-feather": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/react-feather/-/react-feather-1.1.1.tgz", @@ -21143,26 +21220,6 @@ "symbol-observable": "^1.0.4" } }, - "recursive-readdir": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/recursive-readdir/-/recursive-readdir-2.2.1.tgz", - "integrity": "sha1-kO8jHQd4xc4JPJpI105cVCLROpk=", - "dev": true, - "requires": { - "minimatch": "3.0.3" - }, - "dependencies": { - "minimatch": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.3.tgz", - "integrity": "sha1-Kk5AkLlrLbBqnX3wEFWmKnfJt3Q=", - "dev": true, - "requires": { - "brace-expansion": "^1.0.0" - } - } - } - }, "redent": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/redent/-/redent-2.0.0.tgz", diff --git a/package.json b/package.json index 6b41d9457..363109e90 100644 --- a/package.json +++ b/package.json @@ -126,6 +126,7 @@ "@types/tlds": "^1.199.0", "@types/uglifyjs-webpack-plugin": "^1.1.0", "@types/uuid": "^3.4.3", + "@types/vinyl": "^2.0.2", "@types/webpack": "^4.4.7", "@types/webpack-dev-server": "^2.9.5", "@types/webpack-manifest-plugin": "^1.3.2", From 570bf4815db625fba1d0def397b4d4c3930e74db Mon Sep 17 00:00:00 2001 From: Chi Vinh Le Date: Thu, 30 Aug 2018 15:53:00 +0200 Subject: [PATCH 05/18] Make type checking part of the linting --- package.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/package.json b/package.json index 363109e90..666743414 100644 --- a/package.json +++ b/package.json @@ -15,14 +15,13 @@ "start": "node dist/index.js", "start:development": "ts-node --project ./src/tsconfig.json -r tsconfig-paths/register ./src/index.ts", "start:webpackDevServer": "ts-node ./scripts/start.ts", - "lint": "npm-run-all --parallel lint:*", + "lint": "npm-run-all --parallel lint:* tscheck:*", "lint:server": "tslint --project ./src/tsconfig.json", "lint:client": "tslint --project ./src/core/client/tsconfig.json", "lint:client-embed": "tslint --project ./src/core/client/embed/tsconfig.json", "lint:scripts": "tslint --project ./tsconfig.json", "lint-fix": "npm run lint:server -- --fix && npm run lint:client -- --fix && npm run lint:client-embed -- --fix && npm run lint:scripts -- --fix", "test": "node scripts/test.js --env=jsdom", - "tscheck": "npm-run-all --parallel tscheck:*", "tscheck:server": "tsc --project ./src/tsconfig.json --noEmit", "tscheck:client": "tsc --project ./src/core/client/tsconfig.json --noEmit", "tscheck:client-embed": "tsc --project ./src/core/client/embed/tsconfig.json --noEmit", From 09caad7bdd926dbc28c7776cc00f40c978700a83 Mon Sep 17 00:00:00 2001 From: Chi Vinh Le Date: Thu, 30 Aug 2018 16:08:42 +0200 Subject: [PATCH 06/18] Keep future added values --- src/core/client/auth/components/App.tsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/core/client/auth/components/App.tsx b/src/core/client/auth/components/App.tsx index 103bf95dc..1cdbe1f76 100644 --- a/src/core/client/auth/components/App.tsx +++ b/src/core/client/auth/components/App.tsx @@ -6,8 +6,6 @@ import ResetPasswordContainer from "../containers/ResetPasswordContainer"; import SignInContainer from "../containers/SignInContainer"; import SignUpContainer from "../containers/SignUpContainer"; -// TODO: (cvle) Remove %future added value when we have Relay 1.6 -// https://github.com/facebook/relay/commit/1e87e43add7667a494f7ff4cfa7f03f1ab8d81a2 export type View = | "SIGN_UP" | "SIGN_IN" From b83d965d6c9baf109c7f90a02f9c98290e362c30 Mon Sep 17 00:00:00 2001 From: Chi Vinh Le Date: Thu, 30 Aug 2018 16:15:56 +0200 Subject: [PATCH 07/18] Better naming --- src/core/client/stream/mutations/CreateCommentMutation.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/core/client/stream/mutations/CreateCommentMutation.ts b/src/core/client/stream/mutations/CreateCommentMutation.ts index 0482fd238..7be32b3dd 100644 --- a/src/core/client/stream/mutations/CreateCommentMutation.ts +++ b/src/core/client/stream/mutations/CreateCommentMutation.ts @@ -9,10 +9,10 @@ import { } from "talk-framework/lib/relay"; import { Omit } from "talk-framework/types"; -import { CreateCommentMutation } from "talk-stream/__generated__/CreateCommentMutation.graphql"; +import { CreateCommentMutation as CreateCommentType } from "talk-stream/__generated__/CreateCommentMutation.graphql"; export type CreateCommentInput = Omit< - CreateCommentMutation["variables"]["input"], + CreateCommentType["variables"]["input"], "clientMutationId" >; @@ -41,7 +41,7 @@ let clientMutationId = 0; function commit(environment: Environment, input: CreateCommentInput) { const me = getMe(environment)!; const currentDate = new Date().toISOString(); - return commitMutationPromiseNormalized(environment, { + return commitMutationPromiseNormalized(environment, { mutation, variables: { input: { @@ -90,4 +90,4 @@ export const withCreateCommentMutation = createMutationContainer( export type CreateCommentMutation = ( input: CreateCommentInput -) => Promise; +) => Promise; From 0dcff653444395edfec60fb3da3fcb3155cdff33 Mon Sep 17 00:00:00 2001 From: Chi Vinh Le Date: Thu, 30 Aug 2018 16:43:01 +0200 Subject: [PATCH 08/18] Upgrade react-relay types --- package-lock.json | 4 ++-- package.json | 1 + .../client/framework/lib/relay/QueryRenderer.tsx | 12 +++++++----- .../stream/mutations/CreateCommentMutation.ts | 8 ++++---- .../client/stream/queries/PermalinkViewQuery.tsx | 11 ++++------- src/core/client/stream/queries/StreamQuery.tsx | 14 +++++++------- 6 files changed, 25 insertions(+), 25 deletions(-) diff --git a/package-lock.json b/package-lock.json index 452c8c4b8..d347dd3da 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2069,7 +2069,7 @@ } }, "@types/react-relay": { - "version": "github:coralproject/patched#ba4c8d01bb737e5f073534b32d870294e39cc5a8", + "version": "github:coralproject/patched#c8e07adee83418ca04acfc99a3c542e082d70116", "from": "github:coralproject/patched#types/react-relay", "dev": true }, @@ -8617,7 +8617,7 @@ "dependencies": { "chalk": { "version": "1.1.3", - "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", "dev": true, "requires": { diff --git a/package.json b/package.json index 666743414..9eaedca62 100644 --- a/package.json +++ b/package.json @@ -22,6 +22,7 @@ "lint:scripts": "tslint --project ./tsconfig.json", "lint-fix": "npm run lint:server -- --fix && npm run lint:client -- --fix && npm run lint:client-embed -- --fix && npm run lint:scripts -- --fix", "test": "node scripts/test.js --env=jsdom", + "tscheck": "npm-run-all --parallel tscheck:*", "tscheck:server": "tsc --project ./src/tsconfig.json --noEmit", "tscheck:client": "tsc --project ./src/core/client/tsconfig.json --noEmit", "tscheck:client-embed": "tsc --project ./src/core/client/embed/tsconfig.json --noEmit", diff --git a/src/core/client/framework/lib/relay/QueryRenderer.tsx b/src/core/client/framework/lib/relay/QueryRenderer.tsx index a5845c0b6..cc8acac40 100644 --- a/src/core/client/framework/lib/relay/QueryRenderer.tsx +++ b/src/core/client/framework/lib/relay/QueryRenderer.tsx @@ -3,23 +3,25 @@ import { QueryRenderer, QueryRendererProps as QueryRendererPropsOrig, } from "react-relay"; +import { OperationBase, OperationDefaults } from "relay-runtime"; import { Omit } from "talk-framework/types"; import { TalkContextConsumer } from "../bootstrap/TalkContext"; // Omit environment as we are passing this from the context. -export type QueryRendererProps = Omit< - QueryRendererPropsOrig, - "environment" ->; +export type QueryRendererProps< + T extends OperationBase = OperationDefaults +> = Omit, "environment">; /** * TalkQueryRenderer is a wrappper around Relay's `QueryRenderer`. * It supplies the `environment` from the context and has better * generics type support. */ -class TalkQueryRenderer extends Component> { +class TalkQueryRenderer< + T extends OperationBase = OperationDefaults +> extends Component> { public render() { return ( diff --git a/src/core/client/stream/mutations/CreateCommentMutation.ts b/src/core/client/stream/mutations/CreateCommentMutation.ts index 7be32b3dd..079285b91 100644 --- a/src/core/client/stream/mutations/CreateCommentMutation.ts +++ b/src/core/client/stream/mutations/CreateCommentMutation.ts @@ -9,10 +9,10 @@ import { } from "talk-framework/lib/relay"; import { Omit } from "talk-framework/types"; -import { CreateCommentMutation as CreateCommentType } from "talk-stream/__generated__/CreateCommentMutation.graphql"; +import { CreateCommentMutation as MutationTypes } from "talk-stream/__generated__/CreateCommentMutation.graphql"; export type CreateCommentInput = Omit< - CreateCommentType["variables"]["input"], + MutationTypes["variables"]["input"], "clientMutationId" >; @@ -41,7 +41,7 @@ let clientMutationId = 0; function commit(environment: Environment, input: CreateCommentInput) { const me = getMe(environment)!; const currentDate = new Date().toISOString(); - return commitMutationPromiseNormalized(environment, { + return commitMutationPromiseNormalized(environment, { mutation, variables: { input: { @@ -90,4 +90,4 @@ export const withCreateCommentMutation = createMutationContainer( export type CreateCommentMutation = ( input: CreateCommentInput -) => Promise; +) => Promise; diff --git a/src/core/client/stream/queries/PermalinkViewQuery.tsx b/src/core/client/stream/queries/PermalinkViewQuery.tsx index 3a9b22f71..67bfb98a0 100644 --- a/src/core/client/stream/queries/PermalinkViewQuery.tsx +++ b/src/core/client/stream/queries/PermalinkViewQuery.tsx @@ -7,10 +7,7 @@ import { QueryRenderer, withLocalStateContainer, } from "talk-framework/lib/relay"; -import { - PermalinkViewQueryResponse, - PermalinkViewQueryVariables, -} from "talk-stream/__generated__/PermalinkViewQuery.graphql"; +import { PermalinkViewQuery as QueryTypes } from "talk-stream/__generated__/PermalinkViewQuery.graphql"; import { PermalinkViewQueryLocal as Local } from "talk-stream/__generated__/PermalinkViewQueryLocal.graphql"; import { Spinner } from "talk-ui/components"; @@ -23,7 +20,7 @@ interface InnerProps { export const render = ({ error, props, -}: ReadyState) => { +}: ReadyState) => { if (error) { return
{error.message}
; } @@ -36,7 +33,7 @@ export const render = ({ const PermalinkViewQuery: StatelessComponent = ({ local: { commentID }, }) => ( - + query={graphql` query PermalinkViewQuery($commentID: ID!) { comment(id: $commentID) { @@ -45,7 +42,7 @@ const PermalinkViewQuery: StatelessComponent = ({ } `} variables={{ - commentID, + commentID: commentID!, }} render={render} /> diff --git a/src/core/client/stream/queries/StreamQuery.tsx b/src/core/client/stream/queries/StreamQuery.tsx index 41d262428..1d0830992 100644 --- a/src/core/client/stream/queries/StreamQuery.tsx +++ b/src/core/client/stream/queries/StreamQuery.tsx @@ -5,10 +5,7 @@ import { QueryRenderer, withLocalStateContainer, } from "talk-framework/lib/relay"; -import { - StreamQueryResponse, - StreamQueryVariables, -} from "talk-stream/__generated__/StreamQuery.graphql"; +import { StreamQuery as QueryTypes } from "talk-stream/__generated__/StreamQuery.graphql"; import { StreamQueryLocal as Local } from "talk-stream/__generated__/StreamQueryLocal.graphql"; import { Spinner } from "talk-ui/components"; import StreamContainer from "../containers/StreamContainer"; @@ -17,7 +14,10 @@ interface InnerProps { local: Local; } -export const render = ({ error, props }: ReadyState) => { +export const render = ({ + error, + props, +}: ReadyState) => { if (error) { return
{error.message}
; } @@ -32,7 +32,7 @@ export const render = ({ error, props }: ReadyState) => { const StreamQuery: StatelessComponent = ({ local: { assetID, authRevision }, }) => ( - + query={graphql` query StreamQuery($assetID: ID!, $authRevision: Int!) { asset(id: $assetID) { @@ -47,7 +47,7 @@ const StreamQuery: StatelessComponent = ({ } `} variables={{ - assetID, + assetID: assetID!, authRevision, }} render={render} From 942ce366b8ac6dcd257eb67b17300bc4ddadb889 Mon Sep 17 00:00:00 2001 From: Chi Vinh Le Date: Thu, 30 Aug 2018 23:49:38 +0200 Subject: [PATCH 09/18] Finish porting to relay-1.7 including types --- package-lock.json | 6 +++--- package.json | 2 +- .../client/auth/containers/AppContainer.tsx | 2 +- src/core/client/framework/lib/relay/types.ts | 7 +++++++ .../lib/relay/withFragmentContainer.ts | 20 ++++++++++++++----- .../lib/relay/withLocalStateContainer.tsx | 5 +++-- .../lib/relay/withPaginationContainer.ts | 18 ++++++++--------- .../lib/relay/withRefetchContainer.ts | 10 +++++++--- .../stream/components/PermalinkView.tsx | 3 ++- .../stream/components/ReplyList.spec.tsx | 13 +++++++----- .../client/stream/components/ReplyList.tsx | 5 ++++- .../client/stream/components/Stream.spec.tsx | 17 +++++++++------- src/core/client/stream/components/Stream.tsx | 8 ++++++-- .../client/stream/containers/AppContainer.tsx | 2 +- .../stream/containers/CommentContainer.tsx | 2 +- .../containers/PermalinkButtonContainer.tsx | 2 +- .../containers/PermalinkViewContainer.tsx | 4 +--- .../stream/containers/ReplyListContainer.tsx | 1 - .../stream/containers/StreamContainer.tsx | 1 - .../stream/containers/UserBoxContainer.tsx | 4 ++-- .../stream/queries/PermalinkViewQuery.tsx | 2 +- .../client/stream/queries/StreamQuery.tsx | 10 +++++++++- src/locales/en-US/stream.ftl | 2 ++ 23 files changed, 94 insertions(+), 52 deletions(-) create mode 100644 src/core/client/framework/lib/relay/types.ts diff --git a/package-lock.json b/package-lock.json index d347dd3da..be57c7c13 100644 --- a/package-lock.json +++ b/package-lock.json @@ -24256,9 +24256,9 @@ "dev": true }, "typescript": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.0.1.tgz", - "integrity": "sha512-zQIMOmC+372pC/CCVLqnQ0zSBiY7HHodU7mpQdjiZddek4GMj31I3dUJ7gAs9o65X7mnRma6OokOkc6f9jjfBg==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.0.3.tgz", + "integrity": "sha512-kk80vLW9iGtjMnIv11qyxLqZm20UklzuR2tL0QAnDIygIUIemcZMxlMWudl9OOt76H3ntVzcTiddQ1/pAAJMYg==", "dev": true }, "ua-parser-js": { diff --git a/package.json b/package.json index 9eaedca62..c56144867 100644 --- a/package.json +++ b/package.json @@ -222,7 +222,7 @@ "typed-css-modules": "^0.3.4", "typeface-manuale": "0.0.54", "typeface-source-sans-pro": "0.0.54", - "typescript": "^3.0.0", + "typescript": "^3.0.3", "uglifyjs-webpack-plugin": "^1.2.5", "webpack": "4.12.0", "webpack-cli": "^3.0.2", diff --git a/src/core/client/auth/containers/AppContainer.tsx b/src/core/client/auth/containers/AppContainer.tsx index b100bfc74..7d424672e 100644 --- a/src/core/client/auth/containers/AppContainer.tsx +++ b/src/core/client/auth/containers/AppContainer.tsx @@ -14,7 +14,7 @@ const AppContainer: StatelessComponent = ({ local: { view } }) => { return ; }; -const enhanced = withLocalStateContainer( +const enhanced = withLocalStateContainer( graphql` fragment AppContainerLocal on Local { view diff --git a/src/core/client/framework/lib/relay/types.ts b/src/core/client/framework/lib/relay/types.ts new file mode 100644 index 000000000..07125e251 --- /dev/null +++ b/src/core/client/framework/lib/relay/types.ts @@ -0,0 +1,7 @@ +import { _RefType } from "react-relay"; + +export type FragmentKeys = { + [P in keyof T]: T[P] extends _RefType | null ? P : never +}[keyof T]; + +export type FragmentKeysNoLocal = Exclude, "local">; diff --git a/src/core/client/framework/lib/relay/withFragmentContainer.ts b/src/core/client/framework/lib/relay/withFragmentContainer.ts index 2541b2001..728c375b5 100644 --- a/src/core/client/framework/lib/relay/withFragmentContainer.ts +++ b/src/core/client/framework/lib/relay/withFragmentContainer.ts @@ -1,12 +1,22 @@ -import { createFragmentContainer, GraphQLTaggedNode } from "react-relay"; +import { + _RefType, + createFragmentContainer, + FragmentOrRegularProp, + GraphQLTaggedNode, +} from "react-relay"; import { InferableComponentEnhancerWithProps } from "recompose"; +import { FragmentKeysNoLocal } from "./types"; /** * withFragmentContainer is a curried version of `createFragmentContainers` * from Relay. */ export default ( - fragmentSpec: { [P in keyof T]: GraphQLTaggedNode } -): InferableComponentEnhancerWithProps => ( - component: React.ComponentType -) => createFragmentContainer(component, fragmentSpec) as any; + fragmentSpec: { [P in FragmentKeysNoLocal]: GraphQLTaggedNode } & { + _?: never; + } +): InferableComponentEnhancerWithProps< + { [P in FragmentKeysNoLocal]: T[P] }, + { [P in FragmentKeysNoLocal]: FragmentOrRegularProp } +> => (component: React.ComponentType) => + createFragmentContainer(component, fragmentSpec) as any; diff --git a/src/core/client/framework/lib/relay/withLocalStateContainer.tsx b/src/core/client/framework/lib/relay/withLocalStateContainer.tsx index 421eb7d71..444b67fa2 100644 --- a/src/core/client/framework/lib/relay/withLocalStateContainer.tsx +++ b/src/core/client/framework/lib/relay/withLocalStateContainer.tsx @@ -13,6 +13,7 @@ import { GraphQLTaggedNode, } from "relay-runtime"; +import { _RefType } from "react-relay"; import { withContext } from "../bootstrap"; interface Props { @@ -35,9 +36,9 @@ export const LOCAL_ID = "client:root.local"; * The `fragmentSpec` must be a `Fragment` on the `LOCAL_TYPE` which * must have the `LOCAL_ID`. */ -function withLocalStateContainer( +function withLocalStateContainer( fragmentSpec: GraphQLTaggedNode -): InferableComponentEnhancer<{ local: T }> { +): InferableComponentEnhancer<{ local: _RefType }> { return compose( withContext(({ relayEnvironment }) => ({ relayEnvironment })), hoistStatics((BaseComponent: React.ComponentType) => { diff --git a/src/core/client/framework/lib/relay/withPaginationContainer.ts b/src/core/client/framework/lib/relay/withPaginationContainer.ts index bb2740d01..143c05b44 100644 --- a/src/core/client/framework/lib/relay/withPaginationContainer.ts +++ b/src/core/client/framework/lib/relay/withPaginationContainer.ts @@ -1,24 +1,24 @@ import { ConnectionConfig, createPaginationContainer, + FragmentOrRegularProp, GraphQLTaggedNode, RelayPaginationProp, } from "react-relay"; import { InferableComponentEnhancerWithProps } from "recompose"; +import { FragmentKeysNoLocal } from "./types"; /** * withPaginationContainer is a curried version of `createPaginationContainers` * from Relay. */ -export default ( - fragmentSpec: { [P in keyof T]: GraphQLTaggedNode }, - connectionConfig: ConnectionConfig< - InnerProps, - FragmentVariables, - QueryVariables - > +export default ( + fragmentSpec: { [P in FragmentKeysNoLocal]: GraphQLTaggedNode } & { + _?: never; + }, + connectionConfig: ConnectionConfig ): InferableComponentEnhancerWithProps< - T & { relay: RelayPaginationProp }, - { [P in keyof T]: any } + { [P in FragmentKeysNoLocal]: T[P] } & { relay: RelayPaginationProp }, + { [P in FragmentKeysNoLocal]: FragmentOrRegularProp } > => (component: React.ComponentType) => createPaginationContainer(component, fragmentSpec, connectionConfig) as any; diff --git a/src/core/client/framework/lib/relay/withRefetchContainer.ts b/src/core/client/framework/lib/relay/withRefetchContainer.ts index 9e9989758..9f0618f55 100644 --- a/src/core/client/framework/lib/relay/withRefetchContainer.ts +++ b/src/core/client/framework/lib/relay/withRefetchContainer.ts @@ -1,19 +1,23 @@ import { createRefetchContainer, + FragmentOrRegularProp, GraphQLTaggedNode, RelayRefetchProp, } from "react-relay"; import { InferableComponentEnhancerWithProps } from "recompose"; +import { FragmentKeysNoLocal } from "./types"; /** * withRefetchContainer is a curried version of `createRefetchContainers` * from Relay. */ export default ( - fragmentSpec: { [P in keyof T]: GraphQLTaggedNode }, + fragmentSpec: { [P in FragmentKeysNoLocal]: GraphQLTaggedNode } & { + _?: never; + }, refetchQuery: GraphQLTaggedNode ): InferableComponentEnhancerWithProps< - T & { relay: RelayRefetchProp }, - { [P in keyof T]: any } + { [P in FragmentKeysNoLocal]: T[P] } & { relay: RelayRefetchProp }, + { [P in FragmentKeysNoLocal]: FragmentOrRegularProp } > => (component: React.ComponentType) => createRefetchContainer(component, fragmentSpec, refetchQuery) as any; diff --git a/src/core/client/stream/components/PermalinkView.tsx b/src/core/client/stream/components/PermalinkView.tsx index 362efa562..1747152d7 100644 --- a/src/core/client/stream/components/PermalinkView.tsx +++ b/src/core/client/stream/components/PermalinkView.tsx @@ -1,13 +1,14 @@ import { Localized } from "fluent-react/compat"; import React, { MouseEvent, StatelessComponent } from "react"; +import { PropTypesOf } from "talk-framework/types"; import { Button, Typography } from "talk-ui/components"; import CommentContainer from "../containers/CommentContainer"; import * as styles from "./PermalinkView.css"; export interface PermalinkViewProps { - comment: {} | null; + comment: PropTypesOf["data"] | null; showAllCommentsHref: string | null; onShowAllComments: (e: MouseEvent) => void; } diff --git a/src/core/client/stream/components/ReplyList.spec.tsx b/src/core/client/stream/components/ReplyList.spec.tsx index 76750c8a0..04038673f 100644 --- a/src/core/client/stream/components/ReplyList.spec.tsx +++ b/src/core/client/stream/components/ReplyList.spec.tsx @@ -3,24 +3,27 @@ import { noop } from "lodash"; import React from "react"; import sinon, { SinonSpy } from "sinon"; +import { removeFragmentRefs } from "talk-framework/testHelpers"; import { PropTypesOf } from "talk-framework/types"; import ReplyList from "./ReplyList"; +const ReplyListN = removeFragmentRefs(ReplyList); + it("renders correctly", () => { - const props: PropTypesOf = { + const props: PropTypesOf = { commentID: "comment-id", comments: [{ id: "comment-1" }, { id: "comment-2" }], onShowAll: noop, hasMore: false, disableShowAll: false, }; - const wrapper = shallow(); + const wrapper = shallow(); expect(wrapper).toMatchSnapshot(); }); describe("when there is more", () => { - const props: PropTypesOf = { + const props: PropTypesOf = { commentID: "comment-id", comments: [{ id: "comment-1" }, { id: "comment-2" }], onShowAll: sinon.spy(), @@ -28,7 +31,7 @@ describe("when there is more", () => { disableShowAll: false, }; - const wrapper = shallow(); + const wrapper = shallow(); it("renders a load more button", () => { expect(wrapper).toMatchSnapshot(); }); @@ -41,7 +44,7 @@ describe("when there is more", () => { }); const wrapperDisabledButton = shallow( - + ); it("disables load more button", () => { expect(wrapperDisabledButton).toMatchSnapshot(); diff --git a/src/core/client/stream/components/ReplyList.tsx b/src/core/client/stream/components/ReplyList.tsx index c35e0c32c..cd2be4660 100644 --- a/src/core/client/stream/components/ReplyList.tsx +++ b/src/core/client/stream/components/ReplyList.tsx @@ -2,6 +2,7 @@ import { Localized } from "fluent-react/compat"; import * as React from "react"; import { StatelessComponent } from "react"; +import { PropTypesOf } from "talk-framework/types"; import { Button, HorizontalGutter } from "talk-ui/components"; import CommentContainer from "../containers/CommentContainer"; @@ -9,7 +10,9 @@ import Indent from "./Indent"; export interface ReplyListProps { commentID: string; - comments: ReadonlyArray<{ id: string }>; + comments: ReadonlyArray< + { id: string } & PropTypesOf["data"] + >; onShowAll: () => void; hasMore: boolean; disableShowAll: boolean; diff --git a/src/core/client/stream/components/Stream.spec.tsx b/src/core/client/stream/components/Stream.spec.tsx index 14ad6dec5..ff327d798 100644 --- a/src/core/client/stream/components/Stream.spec.tsx +++ b/src/core/client/stream/components/Stream.spec.tsx @@ -3,12 +3,15 @@ import { noop } from "lodash"; import React from "react"; import sinon, { SinonSpy } from "sinon"; +import { removeFragmentRefs } from "talk-framework/testHelpers"; import { PropTypesOf } from "talk-framework/types"; import Stream from "./Stream"; +const StreamN = removeFragmentRefs(Stream); + it("renders correctly", () => { - const props: PropTypesOf = { + const props: PropTypesOf = { assetID: "asset-id", isClosed: false, comments: [{ id: "comment-1" }, { id: "comment-2" }], @@ -17,13 +20,13 @@ it("renders correctly", () => { hasMore: false, user: null, }; - const wrapper = shallow(); + const wrapper = shallow(); expect(wrapper).toMatchSnapshot(); }); describe("when use is logged in", () => { it("renders correctly", () => { - const props: PropTypesOf = { + const props: PropTypesOf = { assetID: "asset-id", isClosed: false, comments: [{ id: "comment-1" }, { id: "comment-2" }], @@ -32,13 +35,13 @@ describe("when use is logged in", () => { hasMore: false, user: {}, }; - const wrapper = shallow(); + const wrapper = shallow(); expect(wrapper).toMatchSnapshot(); }); }); describe("when there is more", () => { - const props: PropTypesOf = { + const props: PropTypesOf = { assetID: "asset-id", isClosed: false, comments: [{ id: "comment-1" }, { id: "comment-2" }], @@ -48,7 +51,7 @@ describe("when there is more", () => { user: null, }; - const wrapper = shallow(); + const wrapper = shallow(); it("renders a load more button", () => { expect(wrapper).toMatchSnapshot(); }); @@ -58,7 +61,7 @@ describe("when there is more", () => { expect((props.onLoadMore as SinonSpy).calledOnce).toBe(true); }); - const wrapperDisabledButton = shallow(); + const wrapperDisabledButton = shallow(); it("disables load more button", () => { expect(wrapperDisabledButton).toMatchSnapshot(); }); diff --git a/src/core/client/stream/components/Stream.tsx b/src/core/client/stream/components/Stream.tsx index 884157e36..2d1358f64 100644 --- a/src/core/client/stream/components/Stream.tsx +++ b/src/core/client/stream/components/Stream.tsx @@ -2,6 +2,7 @@ import { Localized } from "fluent-react/compat"; import * as React from "react"; import { StatelessComponent } from "react"; +import { PropTypesOf } from "talk-framework/types"; import { Button, HorizontalGutter } from "talk-ui/components"; import CommentContainer from "../containers/CommentContainer"; @@ -14,11 +15,14 @@ import * as styles from "./Stream.css"; export interface StreamProps { assetID: string; isClosed?: boolean; - comments: ReadonlyArray<{ id: string }>; + comments: ReadonlyArray< + { id: string } & PropTypesOf["data"] & + PropTypesOf["comment"] + >; onLoadMore?: () => void; hasMore?: boolean; disableLoadMore?: boolean; - user: {} | null; + user: PropTypesOf["user"] | null; } const Stream: StatelessComponent = props => { diff --git a/src/core/client/stream/containers/AppContainer.tsx b/src/core/client/stream/containers/AppContainer.tsx index 7fbc7f6d8..533e8e37f 100644 --- a/src/core/client/stream/containers/AppContainer.tsx +++ b/src/core/client/stream/containers/AppContainer.tsx @@ -16,7 +16,7 @@ const AppContainer: StatelessComponent = ({ return ; }; -const enhanced = withLocalStateContainer( +const enhanced = withLocalStateContainer( graphql` fragment AppContainerLocal on Local { commentID diff --git a/src/core/client/stream/containers/CommentContainer.tsx b/src/core/client/stream/containers/CommentContainer.tsx index 0316112d1..4c0a97744 100644 --- a/src/core/client/stream/containers/CommentContainer.tsx +++ b/src/core/client/stream/containers/CommentContainer.tsx @@ -28,7 +28,7 @@ export const CommentContainer: StatelessComponent = props => { return ; }; -const enhanced = withFragmentContainer<{ data: Data }>({ +const enhanced = withFragmentContainer({ data: graphql` fragment CommentContainer on Comment { ...CommentContainer_comment @relay(mask: false) diff --git a/src/core/client/stream/containers/PermalinkButtonContainer.tsx b/src/core/client/stream/containers/PermalinkButtonContainer.tsx index fd1509117..babb4197f 100644 --- a/src/core/client/stream/containers/PermalinkButtonContainer.tsx +++ b/src/core/client/stream/containers/PermalinkButtonContainer.tsx @@ -19,7 +19,7 @@ export const PermalinkContainer: StatelessComponent = ({ ) : null; }; -const enhanced = withLocalStateContainer( +const enhanced = withLocalStateContainer( graphql` fragment PermalinkButtonContainerLocal on Local { assetURL diff --git a/src/core/client/stream/containers/PermalinkViewContainer.tsx b/src/core/client/stream/containers/PermalinkViewContainer.tsx index 096785e27..29e08c61e 100644 --- a/src/core/client/stream/containers/PermalinkViewContainer.tsx +++ b/src/core/client/stream/containers/PermalinkViewContainer.tsx @@ -52,9 +52,7 @@ const enhanced = withContext(ctx => ({ pym: ctx.pym, }))( withSetCommentIDMutation( - withFragmentContainer<{ - comment: CommentData | null; - }>({ + withFragmentContainer({ comment: graphql` fragment PermalinkViewContainer_comment on Comment { ...CommentContainer diff --git a/src/core/client/stream/containers/ReplyListContainer.tsx b/src/core/client/stream/containers/ReplyListContainer.tsx index 4ee7165e6..e2d409a6d 100644 --- a/src/core/client/stream/containers/ReplyListContainer.tsx +++ b/src/core/client/stream/containers/ReplyListContainer.tsx @@ -67,7 +67,6 @@ interface FragmentVariables { } const enhanced = withPaginationContainer< - { comment: Data }, InnerProps, FragmentVariables, ReplyListContainerPaginationQueryVariables diff --git a/src/core/client/stream/containers/StreamContainer.tsx b/src/core/client/stream/containers/StreamContainer.tsx index d629c35d1..dc5f1409f 100644 --- a/src/core/client/stream/containers/StreamContainer.tsx +++ b/src/core/client/stream/containers/StreamContainer.tsx @@ -64,7 +64,6 @@ interface FragmentVariables { } const enhanced = withPaginationContainer< - { asset: AssetData; user: UserData | null }, InnerProps, FragmentVariables, StreamContainerPaginationQueryVariables diff --git a/src/core/client/stream/containers/UserBoxContainer.tsx b/src/core/client/stream/containers/UserBoxContainer.tsx index cc05e3872..6a390c63b 100644 --- a/src/core/client/stream/containers/UserBoxContainer.tsx +++ b/src/core/client/stream/containers/UserBoxContainer.tsx @@ -78,7 +78,7 @@ export class UserBoxContainer extends Component { const enhanced = withSignOutMutation( withSetAuthPopupStateMutation( withShowAuthPopupMutation( - withLocalStateContainer( + withLocalStateContainer( graphql` fragment UserBoxContainerLocal on Local { authPopup { @@ -89,7 +89,7 @@ const enhanced = withSignOutMutation( } ` )( - withFragmentContainer<{ user: UserData | null }>({ + withFragmentContainer({ user: graphql` fragment UserBoxContainer_user on User { username diff --git a/src/core/client/stream/queries/PermalinkViewQuery.tsx b/src/core/client/stream/queries/PermalinkViewQuery.tsx index 67bfb98a0..32943ea17 100644 --- a/src/core/client/stream/queries/PermalinkViewQuery.tsx +++ b/src/core/client/stream/queries/PermalinkViewQuery.tsx @@ -48,7 +48,7 @@ const PermalinkViewQuery: StatelessComponent = ({ /> ); -const enhanced = withLocalStateContainer( +const enhanced = withLocalStateContainer( graphql` fragment PermalinkViewQueryLocal on Local { commentID diff --git a/src/core/client/stream/queries/StreamQuery.tsx b/src/core/client/stream/queries/StreamQuery.tsx index 1d0830992..67a5ae012 100644 --- a/src/core/client/stream/queries/StreamQuery.tsx +++ b/src/core/client/stream/queries/StreamQuery.tsx @@ -1,3 +1,4 @@ +import { Localized } from "fluent-react/compat"; import React, { StatelessComponent } from "react"; import { ReadyState } from "react-relay"; import { @@ -23,6 +24,13 @@ export const render = ({ } if (props) { + if (!props.asset) { + return ( + +
Asset not found
+
+ ); + } return ; } @@ -54,7 +62,7 @@ const StreamQuery: StatelessComponent = ({ /> ); -const enhanced = withLocalStateContainer( +const enhanced = withLocalStateContainer( graphql` fragment StreamQueryLocal on Local { assetID diff --git a/src/locales/en-US/stream.ftl b/src/locales/en-US/stream.ftl index 159ea0b3b..009c52e99 100644 --- a/src/locales/en-US/stream.ftl +++ b/src/locales/en-US/stream.ftl @@ -2,6 +2,8 @@ ## Comments Tab +comments-streamQuery-assetNotFound = Asset not found + comments-postCommentForm-submit = Submit comments-stream-loadMore = Load more comments-replyList-showAll = Show all From aeada424fae13c2c390fbd396cb1f366dece12b4 Mon Sep 17 00:00:00 2001 From: Chi Vinh Le Date: Fri, 31 Aug 2018 02:15:41 +0200 Subject: [PATCH 10/18] Use stock relay-runtime times --- package-lock.json | 5 +++-- package.json | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index be57c7c13..71f83ebd4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2107,8 +2107,9 @@ "dev": true }, "@types/relay-runtime": { - "version": "github:coralproject/patched#c9a2387f6d32f092b9c75ddfca92d1856d2b41f6", - "from": "github:coralproject/patched#types/relay-runtime", + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/@types/relay-runtime/-/relay-runtime-1.3.6.tgz", + "integrity": "sha512-NobpY0XFh0O9FTRkFJsN5ZNd+M+4eac8ZgM5LP3C0tLd1JkfrwuGkyBUMTuDDGYf+T5zl6vTO6EGgXKD+0QpGA==", "dev": true }, "@types/sane": { diff --git a/package.json b/package.json index c56144867..cc589fb99 100644 --- a/package.json +++ b/package.json @@ -120,7 +120,7 @@ "@types/react-responsive": "^3.0.1", "@types/react-test-renderer": "^16.0.1", "@types/recompose": "^0.26.1", - "@types/relay-runtime": "github:coralproject/patched#types/relay-runtime", + "@types/relay-runtime": "^1.3.6", "@types/sane": "^2.0.0", "@types/sinon": "^5.0.1", "@types/tlds": "^1.199.0", From 792ceed2ad5a0c8923ac8884b61d16508e4eb70b Mon Sep 17 00:00:00 2001 From: Chi Vinh Le Date: Fri, 31 Aug 2018 02:28:53 +0200 Subject: [PATCH 11/18] No more patched packages! --- package-lock.json | 11 +++++-- package.json | 2 +- .../lib/relay/withPaginationContainer.ts | 30 +++++++++++++++++-- .../stream/containers/ReplyListContainer.tsx | 4 +-- .../stream/containers/StreamContainer.tsx | 4 +-- 5 files changed, 40 insertions(+), 11 deletions(-) diff --git a/package-lock.json b/package-lock.json index 71f83ebd4..473eb21e2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2069,9 +2069,14 @@ } }, "@types/react-relay": { - "version": "github:coralproject/patched#c8e07adee83418ca04acfc99a3c542e082d70116", - "from": "github:coralproject/patched#types/react-relay", - "dev": true + "version": "1.3.9", + "resolved": "https://registry.npmjs.org/@types/react-relay/-/react-relay-1.3.9.tgz", + "integrity": "sha512-DFhFrEiDUYxR6VWpkf14CnzS7vtpWaVku0EmnxT2X4U45rOUP75nnOEnRCixZyyxIgr7ULpg5Zc1G9VJpQF7GQ==", + "dev": true, + "requires": { + "@types/react": "*", + "@types/relay-runtime": "*" + } }, "@types/react-responsive": { "version": "3.0.1", diff --git a/package.json b/package.json index cc589fb99..ac9113a07 100644 --- a/package.json +++ b/package.json @@ -116,7 +116,7 @@ "@types/query-string": "^6.1.0", "@types/react-copy-to-clipboard": "^4.2.5", "@types/react-dom": "^16.0.6", - "@types/react-relay": "github:coralproject/patched#types/react-relay", + "@types/react-relay": "^1.3.9", "@types/react-responsive": "^3.0.1", "@types/react-test-renderer": "^16.0.1", "@types/recompose": "^0.26.1", diff --git a/src/core/client/framework/lib/relay/withPaginationContainer.ts b/src/core/client/framework/lib/relay/withPaginationContainer.ts index 143c05b44..8535d0a7f 100644 --- a/src/core/client/framework/lib/relay/withPaginationContainer.ts +++ b/src/core/client/framework/lib/relay/withPaginationContainer.ts @@ -1,22 +1,46 @@ import { - ConnectionConfig, + ConnectionData, createPaginationContainer, FragmentOrRegularProp, GraphQLTaggedNode, RelayPaginationProp, } from "react-relay"; import { InferableComponentEnhancerWithProps } from "recompose"; +import { Variables } from "relay-runtime"; import { FragmentKeysNoLocal } from "./types"; +// TODO: (cvle) at some point we might switch these to stock react-relay typings +// when they become versatile enough. +export type FragmentVariablesGetter = ( + prevVars: V, + totalCount: number +) => V; + +export interface ConnectionConfig< + P, + V extends Variables = Variables, + F extends Variables = Variables +> { + direction?: "backward" | "forward"; + getConnectionFromProps?(props: P): ConnectionData | undefined | null; + getFragmentVariables?: FragmentVariablesGetter; + getVariables( + props: P, + paginationInfo: { count: number; cursor?: string }, + fragmentVariables: F + ): V; + query: GraphQLTaggedNode; +} + /** * withPaginationContainer is a curried version of `createPaginationContainers` * from Relay. */ -export default ( +export default ( fragmentSpec: { [P in FragmentKeysNoLocal]: GraphQLTaggedNode } & { _?: never; }, - connectionConfig: ConnectionConfig + connectionConfig: ConnectionConfig ): InferableComponentEnhancerWithProps< { [P in FragmentKeysNoLocal]: T[P] } & { relay: RelayPaginationProp }, { [P in FragmentKeysNoLocal]: FragmentOrRegularProp } diff --git a/src/core/client/stream/containers/ReplyListContainer.tsx b/src/core/client/stream/containers/ReplyListContainer.tsx index e2d409a6d..de741c60d 100644 --- a/src/core/client/stream/containers/ReplyListContainer.tsx +++ b/src/core/client/stream/containers/ReplyListContainer.tsx @@ -68,8 +68,8 @@ interface FragmentVariables { const enhanced = withPaginationContainer< InnerProps, - FragmentVariables, - ReplyListContainerPaginationQueryVariables + ReplyListContainerPaginationQueryVariables, + FragmentVariables >( { comment: graphql` diff --git a/src/core/client/stream/containers/StreamContainer.tsx b/src/core/client/stream/containers/StreamContainer.tsx index dc5f1409f..ff24de3fb 100644 --- a/src/core/client/stream/containers/StreamContainer.tsx +++ b/src/core/client/stream/containers/StreamContainer.tsx @@ -65,8 +65,8 @@ interface FragmentVariables { const enhanced = withPaginationContainer< InnerProps, - FragmentVariables, - StreamContainerPaginationQueryVariables + StreamContainerPaginationQueryVariables, + FragmentVariables >( { asset: graphql` From 428c35a9311d92897f4e17f49c4fe3a2bf93279c Mon Sep 17 00:00:00 2001 From: Chi Vinh Le Date: Fri, 31 Aug 2018 16:11:25 +0200 Subject: [PATCH 12/18] Hide forward ref and fix tests --- .../framework/lib/relay/hideForwardRef.ts | 10 ++++++++++ .../lib/relay/withFragmentContainer.ts | 12 +++++++++-- .../lib/relay/withPaginationContainer.ts | 20 ++++++++++++++++--- .../lib/relay/withRefetchContainer.ts | 16 ++++++++++++--- .../StreamContainer.spec.tsx.snap | 4 ++++ 5 files changed, 54 insertions(+), 8 deletions(-) create mode 100644 src/core/client/framework/lib/relay/hideForwardRef.ts diff --git a/src/core/client/framework/lib/relay/hideForwardRef.ts b/src/core/client/framework/lib/relay/hideForwardRef.ts new file mode 100644 index 000000000..434faf078 --- /dev/null +++ b/src/core/client/framework/lib/relay/hideForwardRef.ts @@ -0,0 +1,10 @@ +import React, { ComponentType, StatelessComponent } from "react"; + +// TODO: (cvle) This currently a workaround to hide ForwardRef from +// enzyme which does not support it well yet. +export default function hideForwardRef(component: ComponentType) { + const wrapped: StatelessComponent = (props: any) => + React.createElement(component, props); + wrapped.displayName = component.displayName; + return wrapped; +} diff --git a/src/core/client/framework/lib/relay/withFragmentContainer.ts b/src/core/client/framework/lib/relay/withFragmentContainer.ts index 728c375b5..bbac61726 100644 --- a/src/core/client/framework/lib/relay/withFragmentContainer.ts +++ b/src/core/client/framework/lib/relay/withFragmentContainer.ts @@ -5,6 +5,9 @@ import { GraphQLTaggedNode, } from "react-relay"; import { InferableComponentEnhancerWithProps } from "recompose"; +import { wrapDisplayName } from "recompose"; + +import hideForwardRef from "./hideForwardRef"; import { FragmentKeysNoLocal } from "./types"; /** @@ -18,5 +21,10 @@ export default ( ): InferableComponentEnhancerWithProps< { [P in FragmentKeysNoLocal]: T[P] }, { [P in FragmentKeysNoLocal]: FragmentOrRegularProp } -> => (component: React.ComponentType) => - createFragmentContainer(component, fragmentSpec) as any; +> => (component: React.ComponentType) => { + const result = createFragmentContainer(component, fragmentSpec); + result.displayName = wrapDisplayName(component, "Relay"); + // TODO: (cvle) We wrap this currently to hide the ForwardRef which is not + // well supported yet in enzyme. + return hideForwardRef(result) as any; +}; diff --git a/src/core/client/framework/lib/relay/withPaginationContainer.ts b/src/core/client/framework/lib/relay/withPaginationContainer.ts index 8535d0a7f..563a7fcb5 100644 --- a/src/core/client/framework/lib/relay/withPaginationContainer.ts +++ b/src/core/client/framework/lib/relay/withPaginationContainer.ts @@ -5,8 +5,13 @@ import { GraphQLTaggedNode, RelayPaginationProp, } from "react-relay"; -import { InferableComponentEnhancerWithProps } from "recompose"; +import { + InferableComponentEnhancerWithProps, + wrapDisplayName, +} from "recompose"; import { Variables } from "relay-runtime"; + +import hideForwardRef from "./hideForwardRef"; import { FragmentKeysNoLocal } from "./types"; // TODO: (cvle) at some point we might switch these to stock react-relay typings @@ -44,5 +49,14 @@ export default ( ): InferableComponentEnhancerWithProps< { [P in FragmentKeysNoLocal]: T[P] } & { relay: RelayPaginationProp }, { [P in FragmentKeysNoLocal]: FragmentOrRegularProp } -> => (component: React.ComponentType) => - createPaginationContainer(component, fragmentSpec, connectionConfig) as any; +> => (component: React.ComponentType) => { + const result = createPaginationContainer( + component, + fragmentSpec, + connectionConfig + ); + result.displayName = wrapDisplayName(component, "Relay"); + // TODO: (cvle) We wrap this currently to hide the ForwardRef which is not + // well supported yet in enzyme. + return hideForwardRef(result) as any; +}; diff --git a/src/core/client/framework/lib/relay/withRefetchContainer.ts b/src/core/client/framework/lib/relay/withRefetchContainer.ts index 9f0618f55..3036daaf8 100644 --- a/src/core/client/framework/lib/relay/withRefetchContainer.ts +++ b/src/core/client/framework/lib/relay/withRefetchContainer.ts @@ -4,7 +4,12 @@ import { GraphQLTaggedNode, RelayRefetchProp, } from "react-relay"; -import { InferableComponentEnhancerWithProps } from "recompose"; +import { + InferableComponentEnhancerWithProps, + wrapDisplayName, +} from "recompose"; + +import hideForwardRef from "./hideForwardRef"; import { FragmentKeysNoLocal } from "./types"; /** @@ -19,5 +24,10 @@ export default ( ): InferableComponentEnhancerWithProps< { [P in FragmentKeysNoLocal]: T[P] } & { relay: RelayRefetchProp }, { [P in FragmentKeysNoLocal]: FragmentOrRegularProp } -> => (component: React.ComponentType) => - createRefetchContainer(component, fragmentSpec, refetchQuery) as any; +> => (component: React.ComponentType) => { + const result = createRefetchContainer(component, fragmentSpec, refetchQuery); + result.displayName = wrapDisplayName(component, "Relay"); + // TODO: (cvle) We wrap this currently to hide the ForwardRef which is not + // well supported yet in enzyme. + return hideForwardRef(result) as any; +}; diff --git a/src/core/client/stream/containers/__snapshots__/StreamContainer.spec.tsx.snap b/src/core/client/stream/containers/__snapshots__/StreamContainer.spec.tsx.snap index e3813397c..e24710a2b 100644 --- a/src/core/client/stream/containers/__snapshots__/StreamContainer.spec.tsx.snap +++ b/src/core/client/stream/containers/__snapshots__/StreamContainer.spec.tsx.snap @@ -16,6 +16,7 @@ exports[`renders correctly 1`] = ` disableLoadMore={false} isClosed={false} onLoadMore={[Function]} + user={null} /> `; @@ -36,6 +37,7 @@ exports[`when has more comments renders hasMore 1`] = ` hasMore={true} isClosed={false} onLoadMore={[Function]} + user={null} /> `; @@ -56,6 +58,7 @@ exports[`when has more comments when loading more disables load more button 1`] hasMore={true} isClosed={false} onLoadMore={[Function]} + user={null} /> `; @@ -76,5 +79,6 @@ exports[`when has more comments when loading more enable load more button after hasMore={true} isClosed={false} onLoadMore={[Function]} + user={null} /> `; From 25e8cb517b0ce9b9277ac559aa7e6f0ae651f3d7 Mon Sep 17 00:00:00 2001 From: Chi Vinh Le Date: Fri, 31 Aug 2018 17:15:40 +0200 Subject: [PATCH 13/18] Add integration test --- package-lock.json | 40 +- package.json | 2 +- .../framework/testHelpers/createSinonStub.ts | 9 + .../client/framework/testHelpers/index.ts | 1 + .../stream/components/PostCommentForm.tsx | 14 +- .../__snapshots__/postComment.spec.tsx.snap | 557 ++++++++++++++++++ .../client/stream/test/postComment.spec.tsx | 104 ++++ src/core/client/test/jsdom.ts | 9 + 8 files changed, 717 insertions(+), 19 deletions(-) create mode 100644 src/core/client/framework/testHelpers/createSinonStub.ts create mode 100644 src/core/client/stream/test/__snapshots__/postComment.spec.tsx.snap create mode 100644 src/core/client/stream/test/postComment.spec.tsx diff --git a/package-lock.json b/package-lock.json index 473eb21e2..25c60ef44 100644 --- a/package-lock.json +++ b/package-lock.json @@ -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" diff --git a/package.json b/package.json index ac9113a07..f48dcff66 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/src/core/client/framework/testHelpers/createSinonStub.ts b/src/core/client/framework/testHelpers/createSinonStub.ts new file mode 100644 index 000000000..5a7016d90 --- /dev/null +++ b/src/core/client/framework/testHelpers/createSinonStub.ts @@ -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; +} diff --git a/src/core/client/framework/testHelpers/index.ts b/src/core/client/framework/testHelpers/index.ts index 484fb1d65..345e7be68 100644 --- a/src/core/client/framework/testHelpers/index.ts +++ b/src/core/client/framework/testHelpers/index.ts @@ -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, diff --git a/src/core/client/stream/components/PostCommentForm.tsx b/src/core/client/stream/components/PostCommentForm.tsx index e1019ec84..00f544b86 100644 --- a/src/core/client/stream/components/PostCommentForm.tsx +++ b/src/core/client/stream/components/PostCommentForm.tsx @@ -27,13 +27,21 @@ export interface PostCommentFormProps { const PostCommentForm: StatelessComponent = props => (
{({ handleSubmit, submitting }) => ( - + {({ input, meta }) => (
- + Post a comment @@ -42,7 +50,7 @@ const PostCommentForm: StatelessComponent = props => ( attrs={{ placeholder: true }} > input.onChange(html)} value={input.value} placeholder="Post a comment" diff --git a/src/core/client/stream/test/__snapshots__/postComment.spec.tsx.snap b/src/core/client/stream/test/__snapshots__/postComment.spec.tsx.snap new file mode 100644 index 000000000..145f4be33 --- /dev/null +++ b/src/core/client/stream/test/__snapshots__/postComment.spec.tsx.snap @@ -0,0 +1,557 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`post a comment 1`] = ` +
+
+
+
+
+
+ Signed in as + + Markus + + . +
+
+ + Not you?  + + +
+
+
+ +
+
+ +
+
+
+ + + +
+ +
+
+
+
+
+
+ + Powered by + + ⁨The Coral Project⁩ + + +
+ +
+
+ +
+
+
+
+
+ + Markus + + +
+
Hello world", + } + } + /> +
+
+
+
+
+
+ + Markus + + +
+
+
+
+
+
+
+
+ + Lukas + + +
+
+
+
+
+
+
+
+`; + +exports[`renders comment stream 1`] = ` +
+
+
+
+
+
+ Signed in as + + Markus + + . +
+
+ + Not you?  + + +
+
+
+
+
+
+ +
+
+
+ + + +
+ +
+
+
+
+
+
+ + Powered by + + ⁨The Coral Project⁩ + + +
+ +
+
+ +
+
+
+
+
+ + Markus + + +
+
+
+
+
+
+
+
+ + Lukas + + +
+
+
+
+
+
+
+
+`; diff --git a/src/core/client/stream/test/postComment.spec.tsx b/src/core/client/stream/test/postComment.spec.tsx new file mode 100644 index 000000000..0546901db --- /dev/null +++ b/src/core/client/stream/test/postComment.spec.tsx @@ -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: "Hello world!", + clientMutationId: "0", + }, + }) + .returns({ + commentEdge: { + cursor: "2018-07-06T18:24:00.000Z", + node: { + id: "comment-x", + author: users[0], + body: "Hello world", + 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( + + + , + { 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: "Hello world!" }); + + testRenderer.root + .findByProps({ id: "comments-postCommentForm-form" }) + .props.onSubmit(); + + // Wait for loading. + await timeout(); + expect(testRenderer.toJSON()).toMatchSnapshot(); +}); diff --git a/src/core/client/test/jsdom.ts b/src/core/client/test/jsdom.ts index 9e5518731..5ec90c988 100644 --- a/src/core/client/test/jsdom.ts +++ b/src/core/client/test/jsdom.ts @@ -5,6 +5,15 @@ declare var global: any; const jsdom = new JSDOM(""); 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") From 4c1f2e72cb142b1a145cf5e537ffb8df84d6d6a2 Mon Sep 17 00:00:00 2001 From: Chi Vinh Le Date: Fri, 31 Aug 2018 17:24:42 +0200 Subject: [PATCH 14/18] Use createSinonStub --- .../framework/testHelpers/createSinonStub.ts | 20 +++++ src/core/client/stream/test/loadMore.spec.tsx | 87 ++++++++++--------- .../client/stream/test/permalinkView.spec.tsx | 19 ++-- .../permalinkViewCommentNotFound.spec.tsx | 10 +-- .../client/stream/test/renderReplies.spec.tsx | 14 +-- .../client/stream/test/renderStream.spec.tsx | 11 ++- .../stream/test/showAllReplies.spec.tsx | 87 ++++++++++--------- 7 files changed, 135 insertions(+), 113 deletions(-) diff --git a/src/core/client/framework/testHelpers/createSinonStub.ts b/src/core/client/framework/testHelpers/createSinonStub.ts index 5a7016d90..a1d92cddf 100644 --- a/src/core/client/framework/testHelpers/createSinonStub.ts +++ b/src/core/client/framework/testHelpers/createSinonStub.ts @@ -1,5 +1,25 @@ import sinon, { SinonStub } from "sinon"; +/** + * createSinonStub assist in setting up a stub with different + * return paths. + * + * e.g. + * ``` + * const s = sinon.stub(); + * s.throws(); + * s.withArgs("a").returns(0); + * s.withArgs("b").returns(1); + * ``` + * is equivalent to. + * ``` + * const s = createSinonStub( + * s => s.throws(), + * s => s.withArgs("a").returns(0), + * s => s.withArgs("b").returns(1), + * ); + * ``` + */ export default function createSinonStub( ...callbacks: Array<(s: SinonStub) => void> ): SinonStub { diff --git a/src/core/client/stream/test/loadMore.spec.tsx b/src/core/client/stream/test/loadMore.spec.tsx index 5341b39ee..f58a80004 100644 --- a/src/core/client/stream/test/loadMore.spec.tsx +++ b/src/core/client/stream/test/loadMore.spec.tsx @@ -1,12 +1,12 @@ import React from "react"; import TestRenderer, { ReactTestRenderer } from "react-test-renderer"; -import sinon from "sinon"; 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"; @@ -16,54 +16,55 @@ import { assets, comments } from "./fixtures"; let testRenderer: ReactTestRenderer; beforeEach(() => { - const connectionStub = sinon.stub().throws(); - connectionStub.withArgs({ first: 5, orderBy: "CREATED_AT_DESC" }).returns({ - edges: [ - { - node: comments[0], - cursor: comments[0].createdAt, - }, - { - node: comments[1], - cursor: comments[1].createdAt, - }, - ], - pageInfo: { - endCursor: comments[1].createdAt, - hasNextPage: true, - }, - }); - connectionStub - .withArgs({ - first: 10, - orderBy: "CREATED_AT_DESC", - after: comments[1].createdAt, - }) - .returns({ - edges: [ - { - node: comments[2], - cursor: comments[2].createdAt, - }, - ], - pageInfo: { - endCursor: comments[2].createdAt, - hasNextPage: false, - }, - }); - const assetStub = { ...assets[0], - comments: connectionStub, + comments: createSinonStub( + s => s.throws(), + s => + s.withArgs({ first: 5, orderBy: "CREATED_AT_DESC" }).returns({ + edges: [ + { + node: comments[0], + cursor: comments[0].createdAt, + }, + { + node: comments[1], + cursor: comments[1].createdAt, + }, + ], + pageInfo: { + endCursor: comments[1].createdAt, + hasNextPage: true, + }, + }), + s => + s + .withArgs({ + first: 10, + orderBy: "CREATED_AT_DESC", + after: comments[1].createdAt, + }) + .returns({ + edges: [ + { + node: comments[2], + cursor: comments[2].createdAt, + }, + ], + pageInfo: { + endCursor: comments[2].createdAt, + hasNextPage: false, + }, + }) + ), }; const resolvers = { Query: { - asset: sinon - .stub() - .throws() - .withArgs(undefined, { id: assetStub.id }) - .returns(assetStub), + asset: createSinonStub( + s => s.throws(), + s => s.withArgs(undefined, { id: assetStub.id }).returns(assetStub) + ), }, }; diff --git a/src/core/client/stream/test/permalinkView.spec.tsx b/src/core/client/stream/test/permalinkView.spec.tsx index 666f4af5e..c68d3fde3 100644 --- a/src/core/client/stream/test/permalinkView.spec.tsx +++ b/src/core/client/stream/test/permalinkView.spec.tsx @@ -8,6 +8,7 @@ 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"; @@ -38,16 +39,14 @@ beforeEach(() => { const resolvers = { Query: { - comment: sinon - .stub() - .throws() - .withArgs(undefined, { id: commentStub.id }) - .returns(commentStub), - asset: sinon - .stub() - .throws() - .withArgs(undefined, { id: assetStub.id }) - .returns(assetStub), + comment: createSinonStub( + s => s.throws(), + s => s.withArgs(undefined, { id: commentStub.id }).returns(commentStub) + ), + asset: createSinonStub( + s => s.throws(), + s => s.withArgs(undefined, { id: assetStub.id }).returns(assetStub) + ), }, }; diff --git a/src/core/client/stream/test/permalinkViewCommentNotFound.spec.tsx b/src/core/client/stream/test/permalinkViewCommentNotFound.spec.tsx index 368e29bc3..f49dc0534 100644 --- a/src/core/client/stream/test/permalinkViewCommentNotFound.spec.tsx +++ b/src/core/client/stream/test/permalinkViewCommentNotFound.spec.tsx @@ -8,6 +8,7 @@ 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"; @@ -39,11 +40,10 @@ beforeEach(() => { const resolvers = { Query: { comment: () => null, - asset: sinon - .stub() - .throws() - .withArgs(undefined, { id: assetStub.id }) - .returns(assetStub), + asset: createSinonStub( + s => s.throws(), + s => s.withArgs(undefined, { id: assetStub.id }).returns(assetStub) + ), }, }; diff --git a/src/core/client/stream/test/renderReplies.spec.tsx b/src/core/client/stream/test/renderReplies.spec.tsx index 673f6da60..970d1a31b 100644 --- a/src/core/client/stream/test/renderReplies.spec.tsx +++ b/src/core/client/stream/test/renderReplies.spec.tsx @@ -1,13 +1,13 @@ import React from "react"; import TestRenderer, { ReactTestRenderer } from "react-test-renderer"; import { RecordProxy } from "relay-runtime"; -import sinon from "sinon"; 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"; @@ -19,11 +19,13 @@ let testRenderer: ReactTestRenderer; beforeEach(() => { const resolvers = { Query: { - asset: sinon - .stub() - .throws() - .withArgs(undefined, { id: assetWithReplies.id }) - .returns(assetWithReplies), + asset: createSinonStub( + s => s.throws(), + s => + s + .withArgs(undefined, { id: assetWithReplies.id }) + .returns(assetWithReplies) + ), }, }; diff --git a/src/core/client/stream/test/renderStream.spec.tsx b/src/core/client/stream/test/renderStream.spec.tsx index 3e95fbc25..3ea0d74c6 100644 --- a/src/core/client/stream/test/renderStream.spec.tsx +++ b/src/core/client/stream/test/renderStream.spec.tsx @@ -1,13 +1,13 @@ import React from "react"; import TestRenderer, { ReactTestRenderer } from "react-test-renderer"; import { RecordProxy } from "relay-runtime"; -import sinon from "sinon"; 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"; @@ -19,11 +19,10 @@ let testRenderer: ReactTestRenderer; beforeEach(() => { const resolvers = { Query: { - asset: sinon - .stub() - .throws() - .withArgs(undefined, { id: assets[0].id }) - .returns(assets[0]), + asset: createSinonStub( + s => s.throws(), + s => s.withArgs(undefined, { id: assets[0].id }).returns(assets[0]) + ), }, }; diff --git a/src/core/client/stream/test/showAllReplies.spec.tsx b/src/core/client/stream/test/showAllReplies.spec.tsx index 052e38792..a76aab40d 100644 --- a/src/core/client/stream/test/showAllReplies.spec.tsx +++ b/src/core/client/stream/test/showAllReplies.spec.tsx @@ -8,6 +8,7 @@ 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"; @@ -17,41 +18,43 @@ import { assets, comments } from "./fixtures"; let testRenderer: ReactTestRenderer; beforeEach(() => { - const connectionStub = sinon.stub().throws(); - connectionStub.withArgs({ first: 5, orderBy: "CREATED_AT_ASC" }).returns({ - edges: [ - { - node: comments[1], - cursor: comments[1].createdAt, - }, - ], - pageInfo: { - endCursor: comments[1].createdAt, - hasNextPage: true, - }, - }); - connectionStub - .withArgs({ - first: sinon.match(n => n > 10000), - orderBy: "CREATED_AT_ASC", - after: comments[1].createdAt, - }) - .returns({ - edges: [ - { - node: comments[2], - cursor: comments[2].createdAt, - }, - ], - pageInfo: { - endCursor: comments[2].createdAt, - hasNextPage: false, - }, - }); - const commentStub = { ...comments[0], - replies: connectionStub, + replies: createSinonStub( + s => s.throws(), + s => + s.withArgs({ first: 5, orderBy: "CREATED_AT_ASC" }).returns({ + edges: [ + { + node: comments[1], + cursor: comments[1].createdAt, + }, + ], + pageInfo: { + endCursor: comments[1].createdAt, + hasNextPage: true, + }, + }), + s => + s + .withArgs({ + first: sinon.match(n => n > 10000), + orderBy: "CREATED_AT_ASC", + after: comments[1].createdAt, + }) + .returns({ + edges: [ + { + node: comments[2], + cursor: comments[2].createdAt, + }, + ], + pageInfo: { + endCursor: comments[2].createdAt, + hasNextPage: false, + }, + }) + ), }; const assetStub = { @@ -71,16 +74,14 @@ beforeEach(() => { const resolvers = { Query: { - comment: sinon - .stub() - .throws() - .withArgs(undefined, { id: commentStub.id }) - .returns(commentStub), - asset: sinon - .stub() - .throws() - .withArgs(undefined, { id: assetStub.id }) - .returns(assetStub), + comment: createSinonStub( + s => s.throws(), + s => s.withArgs(undefined, { id: commentStub.id }).returns(commentStub) + ), + asset: createSinonStub( + s => s.throws(), + s => s.withArgs(undefined, { id: assetStub.id }).returns(assetStub) + ), }, }; From 70015100498bc9a1b84f99108848c3f4054ea04a Mon Sep 17 00:00:00 2001 From: Chi Vinh Le Date: Fri, 31 Aug 2018 17:34:52 +0200 Subject: [PATCH 15/18] Compile before lint --- .circleci/config.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.circleci/config.yml b/.circleci/config.yml index dd98d8c41..721a4c701 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -45,6 +45,9 @@ jobs: - checkout - attach_workspace: at: ~/coralproject/talk + - run: + name: Compile schemas and types + command: npm run compile - run: name: Perform linting command: npm run lint From 0135cb9cc3d4ab0433298b4ab7cb1735193e8dce Mon Sep 17 00:00:00 2001 From: Chi Vinh Le Date: Fri, 31 Aug 2018 17:40:38 +0200 Subject: [PATCH 16/18] Add todo --- src/core/server/graph/tenant/resolvers/mutation.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/core/server/graph/tenant/resolvers/mutation.ts b/src/core/server/graph/tenant/resolvers/mutation.ts index 002a3e3b9..1fb331602 100644 --- a/src/core/server/graph/tenant/resolvers/mutation.ts +++ b/src/core/server/graph/tenant/resolvers/mutation.ts @@ -3,6 +3,7 @@ import { GQLMutationTypeResolver } from "talk-server/graph/tenant/schema/__gener const Mutation: GQLMutationTypeResolver = { createComment: async (source, { input }, ctx) => { const comment = await ctx.mutators.Comment.create(input); + // TODO: (cvle) tell wyatt to take a look at this :-) return { commentEdge: { cursor: comment.created_at, From 51e6d5b29ef24ab59d46e957107505761d4153b0 Mon Sep 17 00:00:00 2001 From: Chi Vinh Le Date: Fri, 31 Aug 2018 17:43:03 +0200 Subject: [PATCH 17/18] Test optimistic response --- .../__snapshots__/postComment.spec.tsx.snap | 292 +++++++++++++++++- .../client/stream/test/postComment.spec.tsx | 5 +- 2 files changed, 295 insertions(+), 2 deletions(-) diff --git a/src/core/client/stream/test/__snapshots__/postComment.spec.tsx.snap b/src/core/client/stream/test/__snapshots__/postComment.spec.tsx.snap index 145f4be33..69d6009dd 100644 --- a/src/core/client/stream/test/__snapshots__/postComment.spec.tsx.snap +++ b/src/core/client/stream/test/__snapshots__/postComment.spec.tsx.snap @@ -49,6 +49,296 @@ exports[`post a comment 1`] = `
+
+
+
+ +
+
+
+ + + +
+
Hello world!", + } + } + id="comments-postCommentForm-field" + onBlur={[Function]} + onChange={[Function]} + onCut={[Function]} + onFocus={[Function]} + onInput={[Function]} + onKeyDown={[Function]} + onPaste={[Function]} + onSelect={[Function]} + /> +
+
+
+
+
+ + Powered by + + ⁨The Coral Project⁩ + + +
+ +
+
+ +
+
+
+
+
+ + Markus + + +
+
Hello world!", + } + } + /> +
+
+
+
+
+
+ + Markus + + +
+
+
+
+
+
+
+
+ + Lukas + + +
+
+
+
+
+
+
+
+`; + +exports[`post a comment 2`] = ` +
+
+
+
+
+
+ Signed in as + + Markus + + . +
+
+ + Not you?  + + +
+
+
Hello world", + "__html": "Hello world! (from server)", } } /> diff --git a/src/core/client/stream/test/postComment.spec.tsx b/src/core/client/stream/test/postComment.spec.tsx index 0546901db..670609d89 100644 --- a/src/core/client/stream/test/postComment.spec.tsx +++ b/src/core/client/stream/test/postComment.spec.tsx @@ -46,7 +46,7 @@ beforeEach(() => { node: { id: "comment-x", author: users[0], - body: "Hello world", + body: "Hello world! (from server)", createdAt: "2018-07-06T18:24:00.000Z", }, }, @@ -97,8 +97,11 @@ it("post a comment", async () => { testRenderer.root .findByProps({ id: "comments-postCommentForm-form" }) .props.onSubmit(); + // Test optimistic response. + expect(testRenderer.toJSON()).toMatchSnapshot(); // Wait for loading. await timeout(); + // Test after server response. expect(testRenderer.toJSON()).toMatchSnapshot(); }); From a4002b262b1f771bec6b7c0e63e5329ddc400cce Mon Sep 17 00:00:00 2001 From: Chi Vinh Le Date: Fri, 31 Aug 2018 18:34:00 +0200 Subject: [PATCH 18/18] Keep time! --- package-lock.json | 6 ++++++ package.json | 1 + .../stream/test/__snapshots__/postComment.spec.tsx.snap | 6 +++--- src/core/client/stream/test/postComment.spec.tsx | 3 +++ 4 files changed, 13 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 25c60ef44..cca10df6f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -23515,6 +23515,12 @@ "integrity": "sha1-8y6srFoXW+ol1/q1Zas+2HQe9W8=", "dev": true }, + "timekeeper": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/timekeeper/-/timekeeper-2.1.2.tgz", + "integrity": "sha512-fc1DDqbiyz5vxRO4xkiATwfWUw1FV7W20+FJYal1SnoIYgNuB4WNxYLtbG3zjUBwOSk3P4u1TgBAZYG/aqBWMw==", + "dev": true + }, "timers-browserify": { "version": "2.0.10", "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.10.tgz", diff --git a/package.json b/package.json index f48dcff66..13bc586c8 100644 --- a/package.json +++ b/package.json @@ -209,6 +209,7 @@ "simulant": "^0.2.2", "sinon": "^6.1.5", "style-loader": "^0.21.0", + "timekeeper": "^2.1.2", "ts-jest": "^23.0.0", "ts-loader": "^4.4.2", "ts-node": "^6.2.0", diff --git a/src/core/client/stream/test/__snapshots__/postComment.spec.tsx.snap b/src/core/client/stream/test/__snapshots__/postComment.spec.tsx.snap index 69d6009dd..cb4c00a5c 100644 --- a/src/core/client/stream/test/__snapshots__/postComment.spec.tsx.snap +++ b/src/core/client/stream/test/__snapshots__/postComment.spec.tsx.snap @@ -194,10 +194,10 @@ exports[`post a comment 1`] = `
{ .findByProps({ inputId: "comments-postCommentForm-field" }) .props.onChange({ html: "Hello world!" }); + timekeeper.travel(new Date("2018-07-06T18:24:00.000Z")); testRenderer.root .findByProps({ id: "comments-postCommentForm-form" }) .props.onSubmit(); // Test optimistic response. expect(testRenderer.toJSON()).toMatchSnapshot(); + timekeeper.reset(); // Wait for loading. await timeout();