contacts screen

 avatar
unknown
plain_text
4 years ago
20 kB
6
Indexable
/* ------------------------ Imports ------------------------ */
import { FlatList, Platform, TouchableOpacity } from 'react-native'
import React, { useCallback } from 'react'
import { mount } from 'enzyme'
import { MockedProvider } from '@apollo/client/testing'

import ContactScreen from '../../../App/Containers/Onboarding/ContactsScreen'
import { MediumButton } from '../../../App/Components/Buttons/MediumButton'
import ClickableText from '../../../App/Components/ClickableText'
import Header from '../../../App/Components/Header'
import Users from '../../../App/Components/ListOfFriends/ListOfFriends'
import { LargeButton } from '../../../App/Components/Buttons/LargeButton'
import Modal from 'react-native-modal'
import { useIsFocused } from '@react-navigation/native'
// Importing mutations
import mutations from '../../../src/graphql/mutations'
import AsyncStorage from '@react-native-community/async-storage'
import Contacts from 'react-native-contacts'
import { GraphQLError } from 'graphql'
import { number } from 'prop-types'
jest.mock('@react-native-community/async-storage')
/* ------------------------ Props ------------------------ */
const createTestProps = (props) => ({
  navigation: {
    navigate: jest.fn(),
    goBack: jest.fn()
  },
  ...props
})

/* ------------------------ Constants ------------------------ */
let addFriendMutation = false
let addContactsMutation = false
let sendInviteMutation = false

const CONTACTS_INPUT = [
  {
    name: 'Carl Jung',
    email: 'carl-jung@example.com',
    contactNumber: '(555) 555-5555',
    extraInfo: null,
    recordId: '1'
  },
  {
    name: 'Sher Lock',
    email: 'sher@example.com',
    contactNumber: '(999) 999-9999',
    extraInfo: null,
    recordId: '2'
  },
  {
    name: 'abc',
    email: null,
    contactNumber: '(111) 111-1111',
    extraInfo: null,
    recordId: '3'
  },
  {
    name: 'Without Phone',
    email: 'withoutphone@example.com',
    contactNumber: null,
    extraInfo: null,
    recordId: '4'
  }
]

const CONTACTS_OUTPUT = [
  {
    name: 'Carl Jung',
    email: 'carl-jung@example.com',
    contactNumber: '(555) 555-5555',
    userId: '1',
    contactId: '123',
    existingUserId: '001'
  },
  {
    name: 'Sher Lock',
    email: 'sher@example.com',
    contactNumber: '(999) 999-9999',
    userId: '1',
    contactId: '456',
    existingUserId: null
  },
  {
    name: 'abc',
    email: null,
    contactNumber: '(111) 111-1111',
    userId: '1',
    contactId: '789',
    existingUserId: null
  },
  {
    name: 'Without Phone',
    email: 'withoutphone@example.com',
    contactNumber: null,
    userId: '1',
    contactId: '1409',
    existingUserId: null
  }
]

/* ------------------------ Mocks ------------------------ */
const mocks = [
  {
    request: {
      query: mutations.User.ADD_FRIEND,
      variables: {
        users: ['001']
      }
    },
    result: () => {
      addFriendMutation = true
      return {
        data: {
          addFriend: true
        }
      }
    }
  },
  {
    request: {
      query: mutations.Onboarding.ADD_CONTACTS,
      variables: {
        contacts: CONTACTS_INPUT
      }
    },
    result: () => {
      addContactsMutation = true
      return {
        data: {
          AddContacts: CONTACTS_OUTPUT
        }
      }
    }
  },
  {
    request: {
      query: mutations.Onboarding.SEND_INVITE,
      variables: {
        contactIds: ['456'],
        inviteMode: 'EMAIL'
      }
    },
    result: () => {
      sendInviteMutation = true
      return {
        data: {
          SendInvite: true
        }
      }
    }
  },
  {
    request: {
      query: mutations.Onboarding.SEND_INVITE,
      variables: {
        contactIds: ['456', '1409'],
        inviteMode: 'EMAIL'
      }
    },
    result: () => {
      sendInviteMutation = true
      return {
        data: {
          SendInvite: true
        }
      }
    }
  },
  {
    request: {
      query: mutations.Onboarding.SEND_INVITE,
      variables: {
        contactIds: ['789'],
        inviteMode: 'PHONE'
      }
    },
    result: () => {
      sendInviteMutation = true
      return {
        data: {
          SendInvite: true
        }
      }
    }
  }
]
const defaultOutput = {
  data: {
    AddContacts: CONTACTS_OUTPUT
  }
}
const createCustomMock = (contactsOutput = defaultOutput) => {
  return [
    {
      request: {
        query: mutations.User.ADD_FRIEND,
        variables: {
          users: ['001']
        }
      },
      result: () => {
        addFriendMutation = true
        return {
          data: {
            addFriend: true
          }
        }
      }
    },
    {
      request: {
        query: mutations.Onboarding.ADD_CONTACTS,
        variables: {
          contacts: CONTACTS_INPUT
        }
      },
      result: () => {
        addContactsMutation = true
        return contactsOutput
      }
    },
    {
      request: {
        query: mutations.Onboarding.SEND_INVITE,
        variables: {
          contactIds: ['456'],
          inviteMode: 'EMAIL'
        }
      },
      result: () => {
        sendInviteMutation = true
        return {
          data: {
            SendInvite: true
          }
        }
      }
    },
    {
      request: {
        query: mutations.Onboarding.SEND_INVITE,
        variables: {
          contactIds: ['456', '1409'],
          inviteMode: 'EMAIL'
        }
      },
      result: () => {
        sendInviteMutation = true
        return {
          data: {
            SendInvite: true
          }
        }
      }
    },
    {
      request: {
        query: mutations.Onboarding.SEND_INVITE,
        variables: {
          contactIds: ['789'],
          inviteMode: 'PHONE'
        }
      },
      result: () => {
        sendInviteMutation = true
        return {
          data: {
            SendInvite: true
          }
        }
      }
    }
  ]
}
const OriginalImplementations = {
  getContactsByPhoneNumber: (number, callback) => callback(null, Contacts.DUMMY),
  getAll: (callback) => {
    callback(null, Contacts.DUMMY)
  },
  getContactsByEmailAddress: (number, callback) => callback(null, Contacts.DUMMY)
}

function resetImplementations() {
  Contacts.getContactsByEmailAddress.mockImplementation(
    OriginalImplementations.getContactsByPhoneNumber
  )
  Contacts.getAll.mockImplementation(OriginalImplementations.getContactsByPhoneNumber)
  Contacts.getAll.mockImplementation(OriginalImplementations.getAll)
}

/* ------------------------ Test Suite ------------------------ */

describe('Test for Contact Screen', () => {
  let props
  let wrapper
  useIsFocused.mockImplementation(() => true)
  beforeEach(async () => {
    jest.useRealTimers()
    props = createTestProps({})
    console.error = jest.fn()
    wrapper = mount(
      <MockedProvider mocks={mocks} addTypename={false}>
        <ContactScreen {...props} />
      </MockedProvider>
    )

    // Wait for sometime for add contact mutation to execute
    await new Promise((resolve) => setTimeout(resolve, 1000))
    wrapper.update()
  })

  afterEach(() => {
    addFriendMutation = false
    addContactsMutation = false
    sendInviteMutation = false
    wrapper.unmount()
  })

  test('[T-1] Snapshot and Unit tests', () => {
    expect(wrapper).toMatchSnapshot()
    expect(wrapper.find(Header)).toHaveLength(1)
    expect(wrapper.find(MediumButton)).toHaveLength(1)
    expect(wrapper.find(FlatList)).toHaveLength(3)
    expect(wrapper.find(ClickableText)).toHaveLength(3)
    expect(wrapper.find(Modal)).toHaveLength(1)
  })

  test('[T-2] Test for AddContacts Mutation', async () => {
    expect(addContactsMutation).toBeTruthy()
  })

  test('[T-3] Does not select friend if Add not pressed', async () => {
    // Find the continue button and press on it
    const continueButton = wrapper.find(LargeButton).at(0)
    continueButton.props().onPress()
    await new Promise((resolve) => setTimeout(resolve, 0))

    // Check if addFriendMutation is called or not
    expect(addFriendMutation).toBeFalsy()
  })

  test('[T-4] Select Friend if Invite is pressed and Test for SendInvite mutation', async () => {
    // Find the user card in invite section and press on 'invite' button
    const inviteButton = wrapper.find(Users).at(1).find(TouchableOpacity).at(0)
    inviteButton.props().onPress()
    wrapper.update()

    // Find the continue button and click on it to trigger the send invite mutation
    const continueButton = wrapper.find(LargeButton).at(0)
    continueButton.props().onPress()

    // Wait for sometime for mutation to execute
    await new Promise((resolve) => setTimeout(resolve, 1000))
    expect(sendInviteMutation).toBeTruthy()
  })

  test('[T-5] Test for Select All', async () => {
    // Find the invite all button of Invite via email
    const inviteAllButton = wrapper.find(ClickableText).at(1)
    inviteAllButton.props().onPress()
    wrapper.update()

    // Click on the continue button to send the request to all the friends on newzera
    wrapper.find(Modal).find(MediumButton).at(0).props().onPress()
    wrapper.update()

    // Click on the continue button to trigger the mutation
    const continueButton = wrapper.find(LargeButton).at(0)
    continueButton.props().onPress()

    // Wait for mutation to execute
    await new Promise((resolve) => setTimeout(resolve, 1000))
    expect(sendInviteMutation).toBeTruthy()
  })

  test('[T-6] Test for Add Friend mutation', async () => {
    // Find the user card in friends on newzera section and click on "Add"
    const addButton = wrapper.find(Users).at(0).find(TouchableOpacity).at(0)
    addButton.props().onPress()
    wrapper.update()

    // Find the continue button and click on it
    const continueButton = wrapper.find(LargeButton).at(0)
    continueButton.props().onPress()

    // Wait for mutation to execute
    await new Promise((resolve) => setTimeout(resolve, 1000))
    expect(addFriendMutation).toBeTruthy()
  })

  test('[T-7] Test for Add All modal', async () => {
    // Find the 'Add All' text and click on it
    wrapper.find(ClickableText).at(0).props().onPress()
    wrapper.update()

    // Snapshot when modal is open!
    expect(wrapper).toMatchSnapshot()

    // Click on the backdrop to close the modal
    wrapper.find(Modal).at(0).props().onBackdropPress()
    wrapper.update()

    // Snapshot when modal is closed!
    expect(wrapper).toMatchSnapshot()
  })

  test('[T-8] Test for navigation to Welcome Screen', async () => {
    // Find the header and click on skip
    wrapper.find(Header).at(0).props().onPressRight()

    // Check if we are navigating to Welcome Signup screen
    expect(props.navigation.navigate).toHaveBeenCalledWith('WelcomeSignUp')
  })

  test('[T-9] Test for navigation to Follow Content Screen', async () => {
    // Find the header and click on back button
    wrapper.find(Header).at(0).props().onPressBack()

    // Check if we are navigating to Follow content screen
    expect(props.navigation.goBack).toHaveBeenCalledTimes(1)
  })

  test('[T-10] Test for Add All modal close button', async () => {
    // Find the 'Add all' text and click on it
    wrapper.find(ClickableText).at(0).props().onPress()
    wrapper.update()

    // Snapshot when modal is open!
    expect(wrapper).toMatchSnapshot()

    // Close the modal when you click on the cross button
    wrapper.find(Modal).find(TouchableOpacity).at(0).props().onPress()
    wrapper.update()

    // Snapshot when modal is closed!
    expect(wrapper).toMatchSnapshot()
  })

  test('[T-11] Test for button toggle', async () => {
    /* ----------- Toggle check for friends on newzera ----------- */
    wrapper.find(Users).at(0).find(TouchableOpacity).at(0).props().onPress()
    wrapper.update()

    // User has been selected
    expect(wrapper).toMatchSnapshot()

    wrapper.find(Users).at(0).find(TouchableOpacity).at(0).props().onPress()
    wrapper.update()

    // User has been unselected
    expect(wrapper).toMatchSnapshot()

    /* ----------- Toggle check for friends with email ----------- */
    wrapper.find(Users).at(1).find(TouchableOpacity).at(0).props().onPress()
    wrapper.update()

    // User has been selected
    expect(wrapper).toMatchSnapshot()

    wrapper.find(Users).at(1).find(TouchableOpacity).at(0).props().onPress()
    wrapper.update()

    // User has been unselected
    expect(wrapper).toMatchSnapshot()

    /* ----------- Toggle check for friends with contact number only ----------- */
    wrapper.find(Users).at(3).find(TouchableOpacity).at(0).props().onPress()
    wrapper.update()

    // User has been selected
    expect(wrapper).toMatchSnapshot()

    wrapper.find(Users).at(3).find(TouchableOpacity).at(0).props().onPress()
    wrapper.update()

    // User has been unselected
    expect(wrapper).toMatchSnapshot()
  })

  test('[T-12] Test for Select All friends with contact number only', async () => {
    // Find the "Invite all" text in Invite via email and click on it
    const inviteAllButton = wrapper.find(ClickableText).at(2)
    inviteAllButton.props().onPress()

    wrapper.update()

    // Click on the continue button to send the request to all the friends on newzera
    wrapper.find(Modal).find(MediumButton).at(0).props().onPress()
    wrapper.update()

    // Click on the continue button to trigger the mutation
    const continueButton = wrapper.find(LargeButton).at(0)
    continueButton.props().onPress()

    // Wait till the mutation executes
    await new Promise((resolve) => setTimeout(resolve, 1000))
    expect(sendInviteMutation).toBeTruthy()
  })

  test('[T-13] Test for Add Friend mutation with all friends selected', async () => {
    // Find the 'Add all' text and click on it
    wrapper.find(ClickableText).at(0).props().onPress()
    wrapper.update()

    // Click on the continue button to send the request to all the friends on newzera
    wrapper.find(Modal).find(MediumButton).at(0).props().onPress()
    wrapper.update()

    // Click on the continue button to trigger the mutation
    const continueButton = wrapper.find(LargeButton).at(0)
    continueButton.props().onPress()

    // Wait till the mutation executes
    await new Promise((resolve) => setTimeout(resolve, 1000))
    expect(addFriendMutation).toBeTruthy()
  })
  test('Test when Async Storage fails', async () => {
    const error = new Error('Async Error')
    useIsFocused.mockImplementation(() => true)
    AsyncStorage.setItem = jest.fn()
    AsyncStorage.setItem.mockImplementation(async () => {
      throw error
    })
    let asyncErrorCalled = false
    jest.spyOn(console, 'error').mockImplementation((err) => {
      if (err === error) asyncErrorCalled = true
    })
    wrapper = mount(
      <MockedProvider mocks={mocks} addTypename={false}>
        <ContactScreen {...props} />
      </MockedProvider>
    )
    await new Promise((resolve) => setTimeout(resolve, 1000))
    /**
     * No visual feedback is present in the code
     * Will be replaced by a visual feedback if any of it is added in the future
     */
    expect(asyncErrorCalled).toEqual(true)
  })

  test('If getting Contact info fails with DENIED error', async () => {
    useIsFocused.mockImplementation(() => false)
    Contacts.getAll.mockImplementation((cb) => {
      cb('denied', null)
    })
    let deniederrorcalled = false
    jest.spyOn(console, 'error').mockImplementation((err) => {
      if (err == 'denied') deniederrorcalled = true
    })
    wrapper = mount(
      <MockedProvider mocks={mocks} addTypename={false}>
        <ContactScreen {...props} />
      </MockedProvider>
    )
    /**
     * No visual feedback is present in the code
     * Will be replaced by a visual feedback if any of it is added in the future
     */
    expect(deniederrorcalled).toEqual(true)
  })
})

describe('Testing for different cases of Contacts Output', () => {
  beforeEach(() => {
    console.error = jest.fn()
    resetImplementations()
  })
  afterEach(() => {
    addFriendMutation = false
    addContactsMutation = false
    sendInviteMutation = false
  })

  test('Should log an error if data array has an empty object', async () => {
    const contactData = {
      data: {
        AddContacts: [{}]
      }
    }
    let errorcalled = false
    jest.spyOn(console, 'error').mockImplementation((err) => {
      if (err.message === 'Invalid Data') errorcalled = true
    })

    const wrapper = mount(
      <MockedProvider mocks={createCustomMock(contactData)} addTypename={false}>
        <ContactScreen {...createTestProps()} />
      </MockedProvider>
    )
    await new Promise((resolve) => setTimeout(resolve, 1000))
    wrapper.update()
    expect(errorcalled).toBeTruthy()
  })

  test('Should log an error if data array does not have Add contact field', async () => {
    const contactData = {
      data: {}
    }
    let errorcalled = false
    jest.spyOn(console, 'error').mockImplementation((err) => {
      if (err === 'Could not add contacts!') errorcalled = true
    })

    const wrapper = mount(
      <MockedProvider mocks={createCustomMock(contactData)} addTypename={false}>
        <ContactScreen {...createTestProps()} />
      </MockedProvider>
    )
    await new Promise((resolve) => setTimeout(resolve, 1000))
    wrapper.update()
    expect(errorcalled).toBeTruthy()
  })

  test('Should log an error if Add contacts mutation results in error', async () => {
    const contactData = {
      errors: [new GraphQLError('Error!')]
    }
    let errorcalled = false
    jest.spyOn(console, 'error').mockImplementation((err) => {
      if (err === 'Error!') errorcalled = true
    })

    const wrapper = mount(
      <MockedProvider mocks={createCustomMock(contactData)} addTypename={false}>
        <ContactScreen {...createTestProps()} />
      </MockedProvider>
    )
    await new Promise((resolve) => setTimeout(resolve, 1000))
    wrapper.update()
    expect(errorcalled).toBeTruthy()
  })

  test('Should log an error if data array has an empty object', async () => {
    const contactData = {
      data: {
        AddContacts: [
          {
            name: 'Carl Jung',
            email: 'test@example.com',
            contactNumber: '1234',
            userId: '1',
            contactId: '123',
            existingUserId: '001'
          }
        ]
      }
    }

    const wrapper = mount(
      <MockedProvider mocks={createCustomMock(contactData)} addTypename={false}>
        <ContactScreen {...createTestProps()} />
      </MockedProvider>
    )
    await new Promise((resolve) => setTimeout(resolve, 1000))
    wrapper.update()
  })

  test('User list should be empty when no matching contacts are there ', async () => {
    Contacts.getContactsByPhoneNumber.mockImplementation((number, cb) => cb(null, []))
    Contacts.getContactsByEmailAddress.mockImplementation((number, cb) => cb(null, []))
    const wrapper = mount(
      <MockedProvider mocks={mocks} addTypename={false}>
        <ContactScreen {...createTestProps()} />
      </MockedProvider>
    )
    await new Promise((resolve) => setTimeout(resolve, 2000))
    wrapper.update()

    // Find the user card in friends on newzera section and click on "Add"

    // Find the continue button and click on it
    expect(wrapper.find(Users)).toHaveLength(0)
    const continueButton = wrapper.find(LargeButton).at(0)
    continueButton.props().onPress()
    await new Promise((resolve) => setTimeout(resolve, 0))
    // Wait for mutation to execute
    wrapper.update()
    expect(addFriendMutation).toBeFalsy()
  })

  test('When Platform select returns false', async () => {
    const PlatformSelect = jest.spyOn(Platform, 'select').mockImplementation(() => false)
    const wrapper = mount(
      <MockedProvider mocks={mocks} addTypename={false}>
        <ContactScreen {...createTestProps()} />
      </MockedProvider>
    )
    await new Promise((resolve) => setTimeout(resolve, 2000))
    wrapper.update()
    expect(wrapper).toMatchSnapshot()
  })
})
Editor is loading...