import RTE from "@coralproject/rte" ; import { ReactTestInstance } from "react-test-renderer" ; import sinon from "sinon" ; import timekeeper from "timekeeper" ; import { ERROR_CODES } from "coral-common/errors" ; import { InvalidRequestError , ModerationNudgeError , } from "coral-framework/lib/errors" ; import { GQLResolver } from "coral-framework/schema" ; import { act , createResolversStub , createSinonStub , findParentWithType , wait , waitForElement , within , } from "coral-framework/testHelpers" ; import { baseComment , commenters , settings , stories } from "../../fixtures" ; import create from "./create" ; async function createTestRenderer ( resolver , options = { } ) { const resolvers = { ... resolver , Query : { settings : sinon . stub ( ) . returns ( settings ) , viewer : sinon . stub ( ) . returns ( commenters [ 0 ] ) , stream : sinon . stub ( ) . callsFake ( ( _ , variables ) => { expectAndFail ( variables . id ) . toBe ( stories [ 0 ] . id ) ; return stories [ 0 ] ; } ) , ... resolver . Query , } , } ; const { testRenderer , context } = create ( { logNetwork : false , muteNetworkErrors : options . muteNetworkErrors , resolvers , initLocalState : ( localRecord ) => { localRecord . setValue ( stories [ 0 ] . id , "storyID" ) ; } , } ) ; const tabPane = await waitForElement ( ( ) => within ( testRenderer . root ) . getByTestID ( "current-tab-pane" ) ) ; const rte = await waitForElement ( ( ) => findParentWithType ( within ( tabPane ) . getByLabelText ( "Post a comment" ) , RTE ) ! ) ; const form = findParentWithType ( rte , "form" ) ! ; return { testRenderer , context , tabPane , rte , form , } ; } it ( "post a comment" , async ( ) => { const { rte , form , tabPane } = await createTestRenderer ( { Mutation : { createComment : sinon . stub ( ) . callsFake ( ( _ , data ) => { expectAndFail ( data ) . toMatchObject ( { input : { storyID : stories [ 0 ] . id , body : "<b>Hello world!</b>" , } , } ) ; return { edge : { cursor : "" , node : { ... baseComment , id : "comment-x" , author : commenters [ 0 ] , body : "<b>Hello world! (from server)</b>" , } , } , clientMutationId : data . input . clientMutationId , } ; } ) , } , } ) ; await act ( ( ) => rte . props . onChange ( "<b>Hello world!</b>" ) ) ; timekeeper . freeze ( new Date ( baseComment . createdAt ) ) ; await act ( ( ) => form . props . onSubmit ( ) ) ; timekeeper . reset ( ) ; expect ( within ( within ( tabPane ) . queryAllByTestID ( /^comment-/ ) [ 0 ] ) . toJSON ( ) ) . toMatchSnapshot ( "optimistic response" ) ; await waitForElement ( ( ) => within ( within ( tabPane ) . queryAllByTestID ( /^comment-/ ) [ 0 ] ) . getByText ( "<b>Hello world! (from server)</b>" ) ) ; } ) ; const postACommentAndHandleNonPublishedComment = async ( dismiss ) => { const { rte , form } = await createTestRenderer ( { Mutation : { createComment : sinon . stub ( ) . callsFake ( ( _ , data ) => { expectAndFail ( data ) . toMatchObject ( { input : { storyID : stories [ 0 ] . id , body : "<b>Hello world!</b>" , } , } ) ; return { edge : { cursor : "" , node : { ... baseComment , id : "comment-x" , status : "SYSTEM_WITHHELD" , author : commenters [ 0 ] , body : "<b>Hello world!</b>" , } , } , clientMutationId : data . input . clientMutationId , } ; } ) , } , } ) ; await act ( ( ) => rte . props . onChange ( "<b>Hello world!</b>" ) ) ; await act ( ( ) => form . props . onSubmit ( ) ) ; await waitForElement ( ( ) => within ( form ) . getByText ( "will be reviewed" , { exact : false } ) ) ; await act ( async ( ) => { dismiss ( form , rte ) ; await wait ( ( ) => expect ( within ( form ) . queryByText ( "will be reviewed" , { exact : false } ) ) . toBeNull ( ) ) ; } ) ; } ; it ( "post a comment and handle non-visible comment state (dismiss by click)" , async ( ) => await postACommentAndHandleNonPublishedComment ( ( form , rte ) => { within ( form ) . getByTestID ( "callout-close-button" ) . props . onClick ( ) ; } ) ) ; it ( "post a comment and handle non-visible comment state (dismiss by typing)" , async ( ) => await postACommentAndHandleNonPublishedComment ( ( form , rte ) => { rte . props . onChange ( "Typing..." ) ; } ) ) ; it ( "post a comment and handle server error" , async ( ) => { await act ( async ( ) => { const { form , rte } = await createTestRenderer ( { Mutation : { createComment : sinon . stub ( ) . callsFake ( ( ) => { throw new InvalidRequestError ( { code : ERROR_CODES . INTERNAL_ERROR , traceID : "traceID" , } ) ; } ) , } , } , { muteNetworkErrors : true } ) ; rte . props . onChange ( "<b>Hello world!</b>" ) ; form . props . onSubmit ( ) ; await waitForElement ( ( ) => within ( form ) . getByText ( "INTERNAL_ERROR" ) ) ; } ) ; } ) ; it ( "handle moderation nudge error" , async ( ) => { const { form , rte } = await createTestRenderer ( { Mutation : { createComment : createSinonStub ( ( s ) => s . onFirstCall ( ) . callsFake ( ( _ , data ) => { expectAndFail ( data ) . toMatchObject ( { input : { storyID : stories [ 0 ] . id , body : "<b>Hello world!</b>" , nudge : true , } , } ) ; throw new ModerationNudgeError ( { code : ERROR_CODES . TOXIC_COMMENT , traceID : "traceID" , } ) ; } ) , ( s ) => s . onSecondCall ( ) . callsFake ( ( _ , data ) => { expectAndFail ( data ) . toMatchObject ( { input : { storyID : stories [ 0 ] . id , body : "<b>Hello world!</b>" , nudge : false , } , } ) ; return { edge : { cursor : "" , node : { ... baseComment , id : "comment-x" , status : "SYSTEM_WITHHELD" , author : commenters [ 0 ] , body : "<b>Hello world!</b>" , } , } , clientMutationId : data . input . clientMutationId , } ; } ) ) , } , } , { muteNetworkErrors : true } ) ; await act ( ( ) => rte . props . onChange ( "<b>Hello world!</b>" ) ) ; await act ( ( ) => form . props . onSubmit ( ) ) ; await waitForElement ( ( ) => within ( form ) . getByText ( "TOXIC_COMMENT" ) ) ; await act ( ( ) => form . props . onSubmit ( ) ) ; await waitForElement ( ( ) => within ( form ) . getByText ( "will be reviewed" , { exact : false } ) ) ; } ) ; it ( "handle disabled commenting error" , async ( ) => { let createCommentCalled = false ; const { rte , form } = await createTestRenderer ( createResolversStub < GQLResolver > ( { Mutation : { createComment : ( ) => { createCommentCalled = true ; throw new InvalidRequestError ( { code : ERROR_CODES . COMMENTING_DISABLED , traceID : "traceID" , } ) ; } , } , Query : { settings : ( ) => { if ( ! createCommentCalled ) { return settings ; } return { ... settings , disableCommenting : { enabled : true , message : "commenting disabled" , } , } ; } , } , } ) , { muteNetworkErrors : true } ) ; await act ( ( ) => rte . props . onChange ( "abc" ) ) ; await act ( ( ) => form . props . onSubmit ( ) ) ; await waitForElement ( ( ) => within ( form ) . getByText ( "commenting disabled" ) ) ; expect ( rte . props . disabled ) . toBe ( true ) ; expect ( within ( form ) . getByText ( "Submit" ) . props . disabled ) . toBe ( true ) ; } ) ; it ( "handle story closed" , async ( ) => { let returnStory = stories [ 0 ] ; const { rte , form } = await createTestRenderer ( { Mutation : { createComment : sinon . stub ( ) . callsFake ( ( ) => { throw new InvalidRequestError ( { code : ERROR_CODES . STORY_CLOSED , traceID : "traceID" , } ) ; } ) , } , Query : { story : sinon . stub ( ) . callsFake ( ( ) => returnStory ) , } , } , { muteNetworkErrors : true } ) ; await act ( async ( ) => { rte . props . onChange ( "abc" ) ; form . props . onSubmit ( ) ; returnStory = { ... stories [ 0 ] , isClosed : true } ; await waitForElement ( ( ) => within ( form ) . getByText ( "Story is closed" ) ) ; } ) ; expect ( rte . props . disabled ) . toBe ( true ) ; expect ( within ( form ) . getByText ( "Submit" ) . props . disabled ) . toBe ( true ) ; } ) ;