import { assert } from "chai" ; import * as sinon from "sinon" ; import { TimeoutError } from "../await/timeout-error" ; import { ClientError , ConnectionError , MessageError } from "../client/errors" ; import { assertErrorChain , fakeConnection } from "../helpers.spec" ; import { parseTwitchMessage } from "../message/parser/twitch-message" ; import { JoinMessage } from "../message/twitch-types/membership/join" ; import { joinChannel , JoinError , joinNothingToDo } from "./join" ; describe ( "./operations/join" , function ( ) { describe ( "#joinNotingToDo()" , function ( ) { it ( "should be false if channel is not joined or wanted" , function ( ) { const { client } = fakeConnection ( ) ; client . wantedChannels . clear ( ) ; client . joinedChannels . clear ( ) ; assert . isFalse ( joinNothingToDo ( client , "pajlada" ) ) ; } ) ; it ( "should be false if channel is joined but not wanted" , function ( ) { const { client } = fakeConnection ( ) ; client . wantedChannels . clear ( ) ; client . joinedChannels . clear ( ) ; client . joinedChannels . add ( "pajlada" ) ; assert . isFalse ( joinNothingToDo ( client , "pajlada" ) ) ; } ) ; it ( "should be false if channel is not joined but wanted" , function ( ) { const { client } = fakeConnection ( ) ; client . wantedChannels . clear ( ) ; client . wantedChannels . add ( "pajlada" ) ; client . joinedChannels . clear ( ) ; assert . isFalse ( joinNothingToDo ( client , "pajlada" ) ) ; } ) ; it ( "should be true if channel is joined and wanted" , function ( ) { const { client } = fakeConnection ( ) ; client . wantedChannels . clear ( ) ; client . wantedChannels . add ( "pajlada" ) ; client . joinedChannels . clear ( ) ; assert . isFalse ( joinNothingToDo ( client , "pajlada" ) ) ; } ) ; } ) ; describe ( "#joinChannel()" , function ( ) { it ( "sends the correct wire command" , function ( ) { sinon . useFakeTimers ( ) ; const { data , client } = fakeConnection ( ) ; joinChannel ( client , "pajlada" ) ; assert . deepEqual ( data , [ "JOIN #pajlada\r\n" ] ) ; } ) ; it ( "does nothing if channel is joined and wanted" , function ( ) { sinon . useFakeTimers ( ) ; const { data , client } = fakeConnection ( ) ; client . wantedChannels . add ( "pajlada" ) ; client . joinedChannels . add ( "pajlada" ) ; joinChannel ( client , "pajlada" ) ; assert . deepEqual ( data , [ ] ) ; } ) ; it ( "sends the command if channel is not in joinedChannels but in wantedChannels" , function ( ) { sinon . useFakeTimers ( ) ; const { data , client } = fakeConnection ( ) ; client . wantedChannels . add ( "pajlada" ) ; joinChannel ( client , "pajlada" ) ; assert . deepEqual ( data , [ "JOIN #pajlada\r\n" ] ) ; } ) ; it ( "resolves on incoming JOIN" , async function ( ) { const { emitAndEnd , client , clientError } = fakeConnection ( ) ; const promise = joinChannel ( client , "pajlada" ) ; emitAndEnd ( ":justinfan12345!justinfan12345@justinfan12345.tmi.twitch.tv JOIN #pajlada" , "@emote-only=0;followers-only=5;r9k=0;rituals=0;room-id=11148817;slow=0;subs-only=0 " + ":tmi.twitch.tv ROOMSTATE #pajlada" , ":justinfan12345.tmi.twitch.tv 353 justinfan12345 = #pajlada :justinfan12345" , ":justinfan12345.tmi.twitch.tv 366 justinfan12345 #pajlada :End of /NAMES list" ) ; assert . deepStrictEqual ( await promise , parseTwitchMessage ( ":justinfan12345!justinfan12345@justinfan12345.tmi.twitch.tv JOIN #pajlada" ) as JoinMessage ) ; await clientError ; } ) ; it ( "adds channel to channel list on success" , async function ( ) { const { emitAndEnd , client , clientError } = fakeConnection ( ) ; const promise = joinChannel ( client , "pajlada" ) ; emitAndEnd ( ":justinfan12345!justinfan12345@justinfan12345.tmi.twitch.tv JOIN #pajlada" , "@emote-only=0;followers-only=5;r9k=0;rituals=0;room-id=11148817;slow=0;subs-only=0 " + ":tmi.twitch.tv ROOMSTATE #pajlada" , ":justinfan12345.tmi.twitch.tv 353 justinfan12345 = #pajlada :justinfan12345" , ":justinfan12345.tmi.twitch.tv 366 justinfan12345 #pajlada :End of /NAMES list" ) ; await Promise . all ( [ promise , clientError ] ) ; assert . deepStrictEqual ( [ ... client . joinedChannels ] , [ "pajlada" ] ) ; } ) ; it ( "only adds to wantedChannels on msg_channel_suspended failure" , async function ( ) { const { client , emitAndEnd , clientError } = fakeConnection ( ) ; const promise = joinChannel ( client , "test" ) ; emitAndEnd ( "@msg-id=msg_channel_suspended :tmi.twitch.tv NOTICE " + "#test :This channel has been suspended." ) ; await assertErrorChain ( promise , JoinError , "Failed to join channel test: Bad response message: @msg-id=msg_cha" + "nnel_suspended :tmi.twitch.tv NOTICE #test :This channel has bee" + "n suspended." , MessageError , "Bad response message: @msg-id=msg_channel_suspended :tmi.twitch.tv NOTICE " + "#test :This channel has been suspended." ) ; await assertErrorChain ( clientError , JoinError , "Failed to join channel test: Bad response message: @msg-id=msg_cha" + "nnel_suspended :tmi.twitch.tv NOTICE #test :This channel has bee" + "n suspended." , MessageError , "Bad response message: @msg-id=msg_channel_suspended :tmi.twitch.tv NOTICE " + "#test :This channel has been suspended." ) ; assert . deepStrictEqual ( [ ... client . wantedChannels ] , [ "test" ] ) ; assert . deepStrictEqual ( [ ... client . joinedChannels ] , [ ] ) ; } ) ; it ( "only adds to wantedChannels on connection close (no error)" , async function ( ) { const { end , client , clientError } = fakeConnection ( ) ; const promise = joinChannel ( client , "pajlada" ) ; end ( ) ; await assertErrorChain ( promise , JoinError , "Failed to join channel pajlada: Connection closed with no error" , ConnectionError , "Connection closed with no error" ) ; await clientError ; assert ( client . closed , "Client should be closed" ) ; assert . deepStrictEqual ( [ ... client . wantedChannels ] , [ "pajlada" ] ) ; assert . deepStrictEqual ( [ ... client . joinedChannels ] , [ ] ) ; } ) ; it ( "only adds to wantedChannels on connection close (with error)" , async function ( ) { const { end , client , clientError } = fakeConnection ( ) ; const promise = joinChannel ( client , "pajlada" ) ; end ( new Error ( "peer reset connection" ) ) ; await assertErrorChain ( promise , JoinError , "Failed to join channel pajlada: Connection closed " + "due to error: Error occurred in transport layer: p" + "eer reset connection" , ConnectionError , "Connection closed due to error: Error occurred in tran" + "sport layer: peer reset connection" , ConnectionError , "Error occurred in transport layer: peer reset connection" , Error , "peer reset connection" ) ; await assertErrorChain ( clientError , ConnectionError , "Error occurred in transport layer: peer reset connection" , Error , "peer reset connection" ) ; assert ( client . closed , "Client should be closed" ) ; assert . deepStrictEqual ( [ ... client . wantedChannels ] , [ "pajlada" ] ) ; assert . deepStrictEqual ( [ ... client . joinedChannels ] , [ ] ) ; } ) ; it ( "should fail on timeout of 2000 ms" , async function ( ) { sinon . useFakeTimers ( ) ; const { client , clientError } = fakeConnection ( ) ; const promise = joinChannel ( client , "test" ) ; sinon . clock . tick ( 2000 ) ; await assertErrorChain ( promise , JoinError , "Failed to join channel test: Timed out after waiting for res" + "ponse for 2000 milliseconds" , TimeoutError , "Timed out after waiting for response for 2000 milliseconds" ) ; await assertErrorChain ( clientError , JoinError , "Failed to join channel test: Timed out after waiting for res" + "ponse for 2000 milliseconds" , TimeoutError , "Timed out after waiting for response for 2000 milliseconds" ) ; } ) ; } ) ; describe ( "JoinError" , function ( ) { it ( "should not be instanceof ConnectionError" , function ( ) { assert . notInstanceOf ( new JoinError ( "test" ) , ConnectionError ) ; } ) ; it ( "should not be instanceof ClientError" , function ( ) { assert . notInstanceOf ( new JoinError ( "test" ) , ClientError ) ; } ) ; } ) ; } ) ;