diff options
Diffstat (limited to 'tests/Language/GraphQL/FragmentSpec.hs')
| -rw-r--r-- | tests/Language/GraphQL/FragmentSpec.hs | 204 |
1 files changed, 204 insertions, 0 deletions
diff --git a/tests/Language/GraphQL/FragmentSpec.hs b/tests/Language/GraphQL/FragmentSpec.hs new file mode 100644 index 0000000..b7902d9 --- /dev/null +++ b/tests/Language/GraphQL/FragmentSpec.hs @@ -0,0 +1,204 @@ +{- This Source Code Form is subject to the terms of the Mozilla Public License, + v. 2.0. If a copy of the MPL was not distributed with this file, You can + obtain one at https://mozilla.org/MPL/2.0/. -} + +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE QuasiQuotes #-} +module Language.GraphQL.FragmentSpec + ( spec + ) where + +import Data.Aeson ((.=)) +import qualified Data.Aeson as Aeson +import qualified Data.HashMap.Strict as HashMap +import Data.Text (Text) +import Language.GraphQL +import Language.GraphQL.Type +import qualified Language.GraphQL.Type.Out as Out +import Language.GraphQL.TH +import Test.Hspec (Spec, describe, it) +import Test.Hspec.GraphQL + +size :: (Text, Value) +size = ("size", String "L") + +circumference :: (Text, Value) +circumference = ("circumference", Int 60) + +garment :: Text -> (Text, Value) +garment typeName = + ("garment", Object $ HashMap.fromList + [ if typeName == "Hat" then circumference else size + , ("__typename", String typeName) + ] + ) + +inlineQuery :: Text +inlineQuery = [gql| + { + garment { + ... on Hat { + circumference + } + ... on Shirt { + size + } + } + } +|] + +shirtType :: Out.ObjectType IO +shirtType = Out.ObjectType "Shirt" Nothing [] $ HashMap.fromList + [ ("size", sizeFieldType) + ] + +hatType :: Out.ObjectType IO +hatType = Out.ObjectType "Hat" Nothing [] $ HashMap.fromList + [ ("size", sizeFieldType) + , ("circumference", circumferenceFieldType) + ] + +circumferenceFieldType :: Out.Resolver IO +circumferenceFieldType + = Out.ValueResolver (Out.Field Nothing (Out.NamedScalarType int) mempty) + $ pure $ snd circumference + +sizeFieldType :: Out.Resolver IO +sizeFieldType + = Out.ValueResolver (Out.Field Nothing (Out.NamedScalarType string) mempty) + $ pure $ snd size + +toSchema :: Text -> (Text, Value) -> Schema IO +toSchema t (_, resolve) = schema queryType Nothing Nothing mempty + where + garmentType = Out.UnionType "Garment" Nothing [hatType, shirtType] + typeNameField = Out.Field Nothing (Out.NamedScalarType string) mempty + garmentField = Out.Field Nothing (Out.NamedUnionType garmentType) mempty + queryType = + case t of + "circumference" -> hatType + "size" -> shirtType + _ -> Out.ObjectType "Query" Nothing [] + $ HashMap.fromList + [ ("garment", ValueResolver garmentField (pure resolve)) + , ("__typename", ValueResolver typeNameField (pure $ String "Shirt")) + ] + +spec :: Spec +spec = do + describe "Inline fragment executor" $ do + it "chooses the first selection if the type matches" $ do + actual <- graphql (toSchema "Hat" $ garment "Hat") inlineQuery + let expected = HashMap.singleton "data" + $ Aeson.object + [ "garment" .= Aeson.object + [ "circumference" .= (60 :: Int) + ] + ] + in actual `shouldResolveTo` expected + + it "chooses the last selection if the type matches" $ do + actual <- graphql (toSchema "Shirt" $ garment "Shirt") inlineQuery + let expected = HashMap.singleton "data" + $ Aeson.object + [ "garment" .= Aeson.object + [ "size" .= ("L" :: Text) + ] + ] + in actual `shouldResolveTo` expected + + it "embeds inline fragments without type" $ do + let sourceQuery = [gql| + { + circumference + ... { + size + } + } + |] + actual <- graphql (toSchema "circumference" circumference) sourceQuery + let expected = HashMap.singleton "data" + $ Aeson.object + [ "circumference" .= (60 :: Int) + , "size" .= ("L" :: Text) + ] + in actual `shouldResolveTo` expected + + it "evaluates fragments on Query" $ do + let sourceQuery = [gql| + { + ... { + size + } + } + |] + in graphql (toSchema "size" size) `shouldResolve` sourceQuery + + describe "Fragment spread executor" $ do + it "evaluates fragment spreads" $ do + let sourceQuery = [gql| + { + ...circumferenceFragment + } + + fragment circumferenceFragment on Hat { + circumference + } + |] + + actual <- graphql (toSchema "circumference" circumference) sourceQuery + let expected = HashMap.singleton "data" + $ Aeson.object + [ "circumference" .= (60 :: Int) + ] + in actual `shouldResolveTo` expected + + it "evaluates nested fragments" $ do + let sourceQuery = [gql| + { + garment { + ...circumferenceFragment + } + } + + fragment circumferenceFragment on Hat { + ...hatFragment + } + + fragment hatFragment on Hat { + circumference + } + |] + + actual <- graphql (toSchema "Hat" $ garment "Hat") sourceQuery + let expected = HashMap.singleton "data" + $ Aeson.object + [ "garment" .= Aeson.object + [ "circumference" .= (60 :: Int) + ] + ] + in actual `shouldResolveTo` expected + + it "considers type condition" $ do + let sourceQuery = [gql| + { + garment { + ...circumferenceFragment + ...sizeFragment + } + } + fragment circumferenceFragment on Hat { + circumference + } + fragment sizeFragment on Shirt { + size + } + |] + expected = HashMap.singleton "data" + $ Aeson.object + [ "garment" .= Aeson.object + [ "circumference" .= (60 :: Int) + ] + ] + actual <- graphql (toSchema "Hat" $ garment "Hat") sourceQuery + actual `shouldResolveTo` expected |
