我应该如何模拟Apollo客户端以避免此错误?

时间:2019-05-28 16:22:04

标签: mocking graphql jestjs apollo-client

React Native App

所以基本上我收到以下错误:

[Network error]: ReferenceError: XMLHttpRequest is not defined

在我的测试中,我使用的是模拟提供程序,因此我认为模拟阿波罗客户端正在尝试进行一些调用是不正确的。

client.js

import { ApolloClient } from "apollo-client";
import { ApolloLink } from "apollo-link";
import { InMemoryCache } from "apollo-cache-inmemory";
import { createHttpLink } from "apollo-link-http";
import { onError } from "apollo-link-error";

import fetch from "unfetch";

const apolloClient = new ApolloClient({
  link: ApolloLink.from([
    onError(({ graphQLErrors, networkError }) => {
      if (graphQLErrors)
        graphQLErrors.map(({ message, locations, path }) =>
          console.error(
            `[GraphQL error]: Message: ${message}, Location: ${locations}, Path: ${path}`
          )
        );
      if (networkError) console.error(`[Network error]: ${networkError}`);
    }),
    createHttpLink({
      uri: "https://qxlmyzfj27.execute-api.eu-west-1.amazonaws.com/dev/graphql",
      credentials: "same-origin",
      fetch: fetch
    })
  ]),
  cache: new InMemoryCache()
});

export default apolloClient;

RecipeListScreen.js

import React, { Component } from "react";
import {
  StyleSheet,
  View,
  Text,
  Image,
  ImageBackground,
  TouchableHighlight,
  StyleProp,
  TextStyle,
  FlatList,
  ListRenderItem,
  ImageSourcePropType,
  TouchableOpacity,
  TextInput,
  Button
} from "react-native";
import { Query, QueryProps } from "react-apollo";
import { withNavigation } from 'react-navigation';
import { connect, Provider } from 'react-redux';

import { Colors, Metrics, Fonts } from "../themes";
import MainImages from "../images/mainImages/mainImages";
import ApplicationStyles from "../styles/ApplicationStyles";
import apolloClient from "../graphql/client";
import {GET_RECIPIES_QUERY, GetRecipesQueryResult} from "../graphql/queries";
import Images from "../images/mainImages/mainImages";
import store from "../components/Store";

class RecipeListScreen extends Component {
  _onPressButton = (item) => {
    this.props.navigation.navigate(
      'RecipeDetail', {
        recipeId: item.recipeId, mainImageEntry: item.mainImageEntry
      } 
    );
  }

  _renderRecipe = ({item}) => {
    return (
      <TouchableOpacity testID={`${item.recipeName}`} onPress={() => this._onPressButton(item)}>
        <View style={styles.recipeContainer}>
          <ImageBackground
            source={Images[item.mainImageEntry]}
            style={styles.recipeImage}
            resizeMode='cover'
          > 
            <View style={styles.textContainer}>
              <Text style={styles.recipeName}>{item.recipeName.toUpperCase()}</Text>
            </View>
          </ImageBackground>
        </View>
      </TouchableOpacity>
    );
  }

  _filterRecipes = (recipes, search) => {
    if (this.props.favoritesFilter) {
      return recipes.filter(recipe => {
        return this.props.selectedFavorites.includes(recipe.recipeId)
      })
    }

    return recipes.filter(recipe => {
      return recipe.recipeName.includes(search)
    })
  }

  render() {
    return (
      <View style={ApplicationStyles.mainContainer}>
        <Query client={apolloClient} query={GET_RECIPIES_QUERY}>
          {({ loading, error, data }) => {
            if (loading) return <Text>Loading...</Text>
            if (error) {
              return <Text>Error</Text>
            }

            const filteredRecipes = this._filterRecipes(
              data.getRecipes,
              this.props.searchFilter.toLowerCase()
            );

            return (
              <View >
                <TextInput
                  testID='InputSearch'
                  placeholder={"Search for some recipes!"}
                  style={styles.searchInput}
                  value={this.props.searchFilter}
                  onChangeText={(textValue) => {
                    this.props.onSearchUpdated(textValue)
                  }}
                />
                <View>
                  <Button testID='FavoritesButton' title="Show my favorites"
                    onPress={() => this.props.toggleFavFilter()}
                  />
                </View>
                {filteredRecipes.length ?
                  <FlatList
                    data={filteredRecipes}
                    testID='FlatList'
                    keyExtractor={(item, index) => `${index}`}
                    renderItem={this._renderRecipe}
                    numColumns={1}
                    horizontal={false}
                  />
                  : <View style={styles.nothingMessageContainer}>
                    <Text testID='NothingFound' style={styles.nothingMessage}>Nothing was found</Text>
                  </View>}

              </View>
            )
          }}
        </Query>
      </View>
    );
  }
}

const styles = StyleSheet.create({
  recipeContainer: {
    flex: 1,
    margin: Metrics.baseMargin,
    justifyContent: 'center',
    alignItems: 'center',
  },
  recipeImage: {
    width: Metrics.images.main,
    height: Metrics.images.main,
  },
  searchInput: {
    backgroundColor: Colors.white,
    borderWidth: Metrics.borderWidth.small,
    borderColor: Colors.grayLight,
    margin: Metrics.baseMargin,
    padding: Metrics.basePadding
  },
  textContainer: {
    flex: 1,
    alignItems: 'center',
    justifyContent: 'center',
    backgroundColor: Colors.transparentWhite
  },
  recipeName: {
    fontSize: Fonts.size.large,
    fontWeight: 'bold',
    color: Colors.grayDark
  },
  nothingMessageContainer: {
    alignItems: 'center',
    justifyContent: 'center',
  },
  nothingMessage: {
    fontSize: Fonts.size.medium
  }
})

mapStateToProps = (state) => {
  return {
    searchFilter: state.searchFilter,
    favoritesFilter: state.toggleFilterForFavorites,
    selectedFavorites: state.selectedFavorites
  }
}

mapDispatchToProps = (dispatch) => {
  return {
    onSearchUpdated: (textValue) => {
      dispatch({
        type: 'ADD_SEARCH_FILTER',
        searchFilter: textValue
      })
    },
    toggleFavFilter: () => {
      dispatch({
        type: 'TOGGLE_FILTER_FOR_FAVORITES'
      })
    }
  }
}

export const RecipeListScreenWithoutHocs = RecipeListScreen;

export default withNavigation(connect(mapStateToProps, mapDispatchToProps)(RecipeListScreen));

RecipeListScreen.test.js的一部分

it('renders correctly', async () => {
    const screen = renderer.create(
        <MockedProvider mocks={mocks} addTypename={false}>
            <RecipeListScreenWithoutHocs />
        </MockedProvider>
    )
    // renders Loading... as expected
    console.log(screen.root.findByType(Text).props);
    // renders Error
    await wait(0);
    console.log(screen.root.findByType(Text).props);
})

这是那些console.logs的输出:

console.log __tests__/RecipeListScreen.test.js:86
      { children: 'Loading...' }
console.error src/graphql/client.ts:18
      [Network error]: ReferenceError: XMLHttpRequest is not defined
console.log __tests__/RecipeListScreen.test.js:88
      { children: 'Error' }

我不确定这是否可能与嘲笑ApolloLink有关。

0 个答案:

没有答案