2021-10-22 12:59:51 +03:00
|
|
|
{-# LANGUAGE TypeApplications #-}
|
|
|
|
|
2021-08-22 20:25:55 +03:00
|
|
|
module AnalyzerTest where
|
|
|
|
|
2022-01-09 01:50:48 +03:00
|
|
|
import Analyzer.TestUtil (ctx)
|
2022-05-13 19:30:55 +03:00
|
|
|
import qualified Data.Aeson as Aeson
|
2021-10-22 12:59:51 +03:00
|
|
|
import Data.Either (isRight)
|
2022-01-09 01:50:48 +03:00
|
|
|
import Data.List (intercalate)
|
2022-01-20 13:45:14 +03:00
|
|
|
import Data.Maybe (fromJust)
|
|
|
|
import qualified StrongPath as SP
|
2021-08-22 20:25:55 +03:00
|
|
|
import Test.Tasty.Hspec
|
2021-11-11 15:26:20 +03:00
|
|
|
import Wasp.Analyzer
|
2022-01-09 01:52:26 +03:00
|
|
|
import Wasp.Analyzer.Parser (Ctx)
|
2021-11-11 15:26:20 +03:00
|
|
|
import qualified Wasp.Analyzer.TypeChecker as TC
|
2021-11-25 14:36:42 +03:00
|
|
|
import qualified Wasp.AppSpec.Action as Action
|
2021-11-11 15:26:20 +03:00
|
|
|
import qualified Wasp.AppSpec.App as App
|
2021-11-25 01:18:25 +03:00
|
|
|
import qualified Wasp.AppSpec.App.Auth as Auth
|
2022-05-20 15:49:26 +03:00
|
|
|
import qualified Wasp.AppSpec.App.Client as Client
|
2021-11-25 16:02:26 +03:00
|
|
|
import qualified Wasp.AppSpec.App.Db as Db
|
2021-11-26 17:26:03 +03:00
|
|
|
import qualified Wasp.AppSpec.App.Dependency as Dependency
|
2023-03-24 14:42:22 +03:00
|
|
|
import qualified Wasp.AppSpec.App.EmailSender as EmailSender
|
2021-11-25 16:02:26 +03:00
|
|
|
import qualified Wasp.AppSpec.App.Server as Server
|
2022-11-01 01:00:03 +03:00
|
|
|
import qualified Wasp.AppSpec.App.Wasp as Wasp
|
2021-11-11 15:26:20 +03:00
|
|
|
import Wasp.AppSpec.Core.Ref (Ref (..))
|
2021-11-25 01:18:25 +03:00
|
|
|
import Wasp.AppSpec.Entity (Entity)
|
|
|
|
import qualified Wasp.AppSpec.Entity as Entity
|
2021-11-25 12:47:50 +03:00
|
|
|
import Wasp.AppSpec.ExtImport (ExtImport (..), ExtImportName (..))
|
2022-05-13 19:30:55 +03:00
|
|
|
import qualified Wasp.AppSpec.JSON as JSON
|
2022-04-06 17:07:45 +03:00
|
|
|
import qualified Wasp.AppSpec.Job as Job
|
2021-11-11 15:26:20 +03:00
|
|
|
import qualified Wasp.AppSpec.Page as Page
|
2021-11-25 14:26:48 +03:00
|
|
|
import qualified Wasp.AppSpec.Query as Query
|
2021-11-25 14:02:02 +03:00
|
|
|
import Wasp.AppSpec.Route (Route)
|
|
|
|
import qualified Wasp.AppSpec.Route as Route
|
2021-11-26 17:26:03 +03:00
|
|
|
import qualified Wasp.Psl.Ast.Model as PslModel
|
2022-11-01 01:00:03 +03:00
|
|
|
import qualified Wasp.Version as WV
|
2021-08-22 20:25:55 +03:00
|
|
|
|
|
|
|
spec_Analyzer :: Spec
|
|
|
|
spec_Analyzer = do
|
|
|
|
describe "Analyzer" $ do
|
|
|
|
it "Analyzes a well-typed example" $ do
|
|
|
|
let source =
|
|
|
|
unlines
|
|
|
|
[ "app Todo {",
|
2022-11-01 01:00:03 +03:00
|
|
|
" wasp: {",
|
|
|
|
" version: \"^" ++ show WV.waspVersion ++ "\",",
|
|
|
|
" },",
|
2021-08-22 20:25:55 +03:00
|
|
|
" title: \"Todo App\",",
|
2021-11-25 01:18:25 +03:00
|
|
|
" head: [\"foo\", \"bar\"],",
|
|
|
|
" auth: {",
|
|
|
|
" userEntity: User,",
|
2022-09-06 21:35:59 +03:00
|
|
|
" methods: { usernameAndPassword: {} },",
|
2022-01-11 15:59:13 +03:00
|
|
|
" onAuthFailedRedirectTo: \"/\",",
|
2021-11-25 14:57:46 +03:00
|
|
|
" },",
|
2021-11-26 17:26:03 +03:00
|
|
|
" dependencies: [",
|
2022-01-11 15:59:13 +03:00
|
|
|
" (\"redux\", \"^4.0.5\")",
|
2021-11-26 17:26:03 +03:00
|
|
|
" ],",
|
2021-11-25 16:02:26 +03:00
|
|
|
" server: {",
|
2022-11-11 19:20:49 +03:00
|
|
|
" setupFn: import { setupServer } from \"@server/bar.js\"",
|
2021-11-25 16:02:26 +03:00
|
|
|
" },",
|
2022-05-20 15:49:26 +03:00
|
|
|
" client: {",
|
2023-02-20 13:15:54 +03:00
|
|
|
" rootComponent: import { App } from \"@client/App.jsx\",",
|
2022-11-11 19:20:49 +03:00
|
|
|
" setupFn: import { setupClient } from \"@client/baz.js\"",
|
2022-05-20 15:49:26 +03:00
|
|
|
" },",
|
2021-11-25 16:02:26 +03:00
|
|
|
" db: {",
|
2023-04-05 12:33:52 +03:00
|
|
|
" system: PostgreSQL,",
|
|
|
|
" seeds: [ import { devSeedSimple } from \"@server/dbSeeds.js\" ]",
|
2023-03-24 14:42:22 +03:00
|
|
|
" },",
|
|
|
|
" emailSender: {",
|
|
|
|
" provider: SendGrid,",
|
|
|
|
" defaultFrom: {",
|
|
|
|
" email: \"test@test.com\",",
|
|
|
|
" name: \"Test\"",
|
|
|
|
" }",
|
2021-11-25 16:02:26 +03:00
|
|
|
" }",
|
2021-10-22 12:59:51 +03:00
|
|
|
"}",
|
2021-11-25 01:18:25 +03:00
|
|
|
"",
|
2021-11-26 17:26:03 +03:00
|
|
|
"entity User {=psl",
|
|
|
|
" description String",
|
|
|
|
"psl=}",
|
2021-11-25 01:18:25 +03:00
|
|
|
"",
|
2021-11-25 12:47:50 +03:00
|
|
|
"page HomePage {",
|
2022-11-11 19:20:49 +03:00
|
|
|
" component: import Home from \"@client/pages/Main\"",
|
2021-11-25 12:47:50 +03:00
|
|
|
"}",
|
|
|
|
"",
|
|
|
|
"page ProfilePage {",
|
2022-11-11 19:20:49 +03:00
|
|
|
" component: import { profilePage } from \"@client/pages/Profile\",",
|
2021-11-25 12:47:50 +03:00
|
|
|
" authRequired: true",
|
2021-11-25 14:02:02 +03:00
|
|
|
"}",
|
|
|
|
"",
|
2022-01-11 15:59:13 +03:00
|
|
|
"route HomeRoute { path: \"/\", to: HomePage }",
|
2021-11-25 14:26:48 +03:00
|
|
|
"",
|
|
|
|
"query getUsers {",
|
2022-11-11 19:20:49 +03:00
|
|
|
" fn: import { getAllUsers } from \"@server/foo.js\",",
|
2021-11-25 14:26:48 +03:00
|
|
|
" entities: [User]",
|
2021-11-25 14:36:42 +03:00
|
|
|
"}",
|
|
|
|
"",
|
|
|
|
"action updateUser {",
|
2022-11-11 19:20:49 +03:00
|
|
|
" fn: import { updateUser } from \"@server/foo.js\",",
|
2021-11-25 14:36:42 +03:00
|
|
|
" entities: [User],",
|
|
|
|
" auth: true",
|
2022-04-06 17:07:45 +03:00
|
|
|
"}",
|
|
|
|
"",
|
|
|
|
"job BackgroundJob {",
|
2022-05-03 21:34:25 +03:00
|
|
|
" executor: PgBoss,",
|
|
|
|
" perform: {",
|
2022-11-11 19:20:49 +03:00
|
|
|
" fn: import { backgroundJob } from \"@server/jobs/baz.js\",",
|
2022-05-13 19:30:55 +03:00
|
|
|
" executorOptions: {",
|
|
|
|
" pgBoss: {=json { \"retryLimit\": 1 } json=}",
|
|
|
|
" }",
|
|
|
|
" },",
|
|
|
|
" schedule: {",
|
|
|
|
" cron: \"*/5 * * * *\",",
|
|
|
|
" args: {=json { \"job\": \"args\" } json=},",
|
|
|
|
" executorOptions: {",
|
|
|
|
" pgBoss: {=json { \"retryLimit\": 0 } json=}",
|
|
|
|
" }",
|
2022-05-03 21:34:25 +03:00
|
|
|
" }",
|
2021-11-25 14:26:48 +03:00
|
|
|
"}"
|
2021-10-22 12:59:51 +03:00
|
|
|
]
|
2021-11-25 14:26:48 +03:00
|
|
|
|
2021-10-22 12:59:51 +03:00
|
|
|
let decls = analyze source
|
2021-11-25 14:26:48 +03:00
|
|
|
|
2021-11-10 18:27:27 +03:00
|
|
|
let expectedApps =
|
|
|
|
[ ( "Todo",
|
|
|
|
App.App
|
2022-11-01 01:00:03 +03:00
|
|
|
{ App.wasp = Wasp.Wasp {Wasp.version = "^" ++ show WV.waspVersion},
|
|
|
|
App.title = "Todo App",
|
2021-11-25 01:18:25 +03:00
|
|
|
App.head = Just ["foo", "bar"],
|
|
|
|
App.auth =
|
|
|
|
Just
|
|
|
|
Auth.Auth
|
|
|
|
{ Auth.userEntity = Ref "User" :: Ref Entity,
|
2022-09-06 21:35:59 +03:00
|
|
|
Auth.externalAuthEntity = Nothing,
|
|
|
|
Auth.methods =
|
|
|
|
Auth.AuthMethods
|
|
|
|
{ Auth.usernameAndPassword = Just Auth.usernameAndPasswordConfig,
|
2022-12-20 17:12:08 +03:00
|
|
|
Auth.google = Nothing,
|
2023-04-06 00:25:03 +03:00
|
|
|
Auth.gitHub = Nothing,
|
|
|
|
Auth.email = Nothing
|
2022-09-06 21:35:59 +03:00
|
|
|
},
|
2022-01-11 15:59:13 +03:00
|
|
|
Auth.onAuthFailedRedirectTo = "/",
|
|
|
|
Auth.onAuthSucceededRedirectTo = Nothing
|
2021-11-25 14:57:46 +03:00
|
|
|
},
|
2021-11-26 17:26:03 +03:00
|
|
|
App.dependencies =
|
|
|
|
Just
|
|
|
|
[ Dependency.Dependency {Dependency.name = "redux", Dependency.version = "^4.0.5"}
|
|
|
|
],
|
2021-11-25 16:02:26 +03:00
|
|
|
App.server =
|
|
|
|
Just
|
|
|
|
Server.Server
|
2022-01-20 13:45:14 +03:00
|
|
|
{ Server.setupFn =
|
|
|
|
Just $
|
|
|
|
ExtImport
|
|
|
|
(ExtImportField "setupServer")
|
|
|
|
(fromJust $ SP.parseRelFileP "bar.js")
|
2021-11-25 16:02:26 +03:00
|
|
|
},
|
2022-05-20 15:49:26 +03:00
|
|
|
App.client =
|
|
|
|
Just
|
|
|
|
Client.Client
|
|
|
|
{ Client.setupFn =
|
|
|
|
Just $
|
2023-02-20 13:15:54 +03:00
|
|
|
ExtImport (ExtImportField "setupClient") (fromJust $ SP.parseRelFileP "baz.js"),
|
|
|
|
Client.rootComponent =
|
|
|
|
Just $
|
|
|
|
ExtImport (ExtImportField "App") (fromJust $ SP.parseRelFileP "App.jsx")
|
2022-05-20 15:49:26 +03:00
|
|
|
},
|
2023-04-05 12:33:52 +03:00
|
|
|
App.db =
|
|
|
|
Just
|
|
|
|
Db.Db
|
|
|
|
{ Db.system = Just Db.PostgreSQL,
|
|
|
|
Db.seeds =
|
|
|
|
Just
|
|
|
|
[ ExtImport
|
|
|
|
(ExtImportField "devSeedSimple")
|
|
|
|
(fromJust $ SP.parseRelFileP "dbSeeds.js")
|
|
|
|
]
|
|
|
|
},
|
2023-03-24 14:42:22 +03:00
|
|
|
App.emailSender =
|
|
|
|
Just
|
|
|
|
EmailSender.EmailSender
|
|
|
|
{ EmailSender.provider = EmailSender.SendGrid,
|
|
|
|
EmailSender.defaultFrom =
|
|
|
|
Just
|
|
|
|
EmailSender.EmailFromField
|
|
|
|
{ EmailSender.email = "test@test.com",
|
|
|
|
EmailSender.name = Just "Test"
|
|
|
|
}
|
|
|
|
}
|
2021-11-10 18:27:27 +03:00
|
|
|
}
|
|
|
|
)
|
|
|
|
]
|
2022-04-06 17:07:45 +03:00
|
|
|
takeDecls <$> decls `shouldBe` Right expectedApps
|
2021-11-25 14:26:48 +03:00
|
|
|
|
2021-11-10 18:27:27 +03:00
|
|
|
let expectedPages =
|
|
|
|
[ ( "HomePage",
|
|
|
|
Page.Page
|
2022-01-20 13:45:14 +03:00
|
|
|
{ Page.component =
|
|
|
|
ExtImport
|
|
|
|
(ExtImportModule "Home")
|
|
|
|
(fromJust $ SP.parseRelFileP "pages/Main"),
|
2021-11-25 12:47:50 +03:00
|
|
|
Page.authRequired = Nothing
|
|
|
|
}
|
|
|
|
),
|
|
|
|
( "ProfilePage",
|
|
|
|
Page.Page
|
2022-01-20 13:45:14 +03:00
|
|
|
{ Page.component =
|
|
|
|
ExtImport
|
|
|
|
(ExtImportField "profilePage")
|
|
|
|
(fromJust $ SP.parseRelFileP "pages/Profile"),
|
2021-11-25 12:47:50 +03:00
|
|
|
Page.authRequired = Just True
|
2021-11-10 18:27:27 +03:00
|
|
|
}
|
|
|
|
)
|
|
|
|
]
|
2022-04-06 17:07:45 +03:00
|
|
|
takeDecls <$> decls `shouldBe` Right expectedPages
|
2021-11-25 14:26:48 +03:00
|
|
|
|
2021-11-25 01:18:25 +03:00
|
|
|
let expectedEntities =
|
|
|
|
[ ( "User",
|
2021-11-26 17:26:03 +03:00
|
|
|
Entity.makeEntity $
|
|
|
|
PslModel.Body
|
|
|
|
[ PslModel.ElementField $
|
|
|
|
PslModel.Field
|
|
|
|
{ PslModel._name = "description",
|
|
|
|
PslModel._type = PslModel.String,
|
|
|
|
PslModel._typeModifiers = [],
|
|
|
|
PslModel._attrs = []
|
|
|
|
}
|
|
|
|
]
|
2021-11-25 01:18:25 +03:00
|
|
|
)
|
|
|
|
]
|
2022-04-06 17:07:45 +03:00
|
|
|
takeDecls <$> decls `shouldBe` Right expectedEntities
|
2021-10-22 12:59:51 +03:00
|
|
|
|
2021-11-25 14:02:02 +03:00
|
|
|
let expectedRoutes =
|
|
|
|
[ ( "HomeRoute",
|
2022-01-11 15:59:13 +03:00
|
|
|
Route.Route {Route.path = "/", Route.to = Ref "HomePage"}
|
2021-11-25 14:02:02 +03:00
|
|
|
)
|
|
|
|
]
|
2022-04-06 17:07:45 +03:00
|
|
|
takeDecls <$> decls `shouldBe` Right expectedRoutes
|
2021-11-25 14:02:02 +03:00
|
|
|
|
2021-11-25 14:26:48 +03:00
|
|
|
let expectedQueries =
|
|
|
|
[ ( "getUsers",
|
|
|
|
Query.Query
|
2022-01-20 13:45:14 +03:00
|
|
|
{ Query.fn =
|
|
|
|
ExtImport
|
|
|
|
(ExtImportField "getAllUsers")
|
|
|
|
(fromJust $ SP.parseRelFileP "foo.js"),
|
2021-11-25 14:36:42 +03:00
|
|
|
Query.entities = Just [Ref "User"],
|
|
|
|
Query.auth = Nothing
|
2021-11-25 14:26:48 +03:00
|
|
|
}
|
|
|
|
)
|
|
|
|
]
|
2022-04-06 17:07:45 +03:00
|
|
|
takeDecls <$> decls `shouldBe` Right expectedQueries
|
2021-11-25 14:26:48 +03:00
|
|
|
|
2021-11-25 14:36:42 +03:00
|
|
|
let expectedAction =
|
|
|
|
[ ( "updateUser",
|
|
|
|
Action.Action
|
2022-01-20 13:45:14 +03:00
|
|
|
{ Action.fn =
|
|
|
|
ExtImport
|
|
|
|
(ExtImportField "updateUser")
|
|
|
|
(fromJust $ SP.parseRelFileP "foo.js"),
|
2021-11-25 14:36:42 +03:00
|
|
|
Action.entities = Just [Ref "User"],
|
|
|
|
Action.auth = Just True
|
|
|
|
}
|
|
|
|
)
|
|
|
|
]
|
2022-04-06 17:07:45 +03:00
|
|
|
takeDecls <$> decls `shouldBe` Right expectedAction
|
|
|
|
|
2022-05-13 19:30:55 +03:00
|
|
|
let jobPerform =
|
|
|
|
Job.Perform
|
|
|
|
( ExtImport
|
|
|
|
(ExtImportField "backgroundJob")
|
|
|
|
(fromJust $ SP.parseRelFileP "jobs/baz.js")
|
|
|
|
)
|
|
|
|
( Just $
|
|
|
|
Job.ExecutorOptions
|
|
|
|
{ Job.pgBoss = JSON.JSON <$> Aeson.decode "{\"retryLimit\":1}",
|
|
|
|
Job.simple = Nothing
|
|
|
|
}
|
|
|
|
)
|
|
|
|
let jobSchedule =
|
|
|
|
Job.Schedule
|
|
|
|
"*/5 * * * *"
|
|
|
|
(JSON.JSON <$> Aeson.decode "{\"job\":\"args\"}")
|
|
|
|
( Just $
|
|
|
|
Job.ExecutorOptions
|
|
|
|
{ Job.pgBoss = JSON.JSON <$> Aeson.decode "{\"retryLimit\":0}",
|
|
|
|
Job.simple = Nothing
|
|
|
|
}
|
|
|
|
)
|
2022-04-06 17:07:45 +03:00
|
|
|
let expectedJob =
|
|
|
|
[ ( "BackgroundJob",
|
|
|
|
Job.Job
|
2022-05-03 21:34:25 +03:00
|
|
|
{ Job.executor = Job.PgBoss,
|
2022-05-13 19:30:55 +03:00
|
|
|
Job.perform = jobPerform,
|
2022-08-17 20:53:05 +03:00
|
|
|
Job.schedule = Just jobSchedule,
|
|
|
|
Job.entities = Nothing
|
2022-04-06 17:07:45 +03:00
|
|
|
}
|
|
|
|
)
|
|
|
|
]
|
|
|
|
takeDecls <$> decls `shouldBe` Right expectedJob
|
2021-11-25 14:36:42 +03:00
|
|
|
|
2021-10-22 12:59:51 +03:00
|
|
|
it "Returns a type error if unexisting declaration is referenced" $ do
|
|
|
|
let source =
|
|
|
|
unlines
|
2022-01-11 15:59:13 +03:00
|
|
|
[ "route HomeRoute { path: \"/\", to: NonExistentPage }"
|
2021-08-22 20:25:55 +03:00
|
|
|
]
|
2022-01-09 01:50:48 +03:00
|
|
|
takeDecls @Route <$> analyze source
|
2022-01-11 15:59:13 +03:00
|
|
|
`shouldBe` Left (TypeError $ TC.mkTypeError (ctx (1, 34) (1, 48)) $ TC.UndefinedIdentifier "NonExistentPage")
|
2021-10-22 12:59:51 +03:00
|
|
|
|
|
|
|
it "Returns a type error if referenced declaration is of wrong type" $ do
|
|
|
|
let source =
|
|
|
|
unlines
|
2022-01-11 15:59:13 +03:00
|
|
|
[ "route HomeRoute { path: \"/\", to: HomeRoute }"
|
2021-10-22 12:59:51 +03:00
|
|
|
]
|
2022-01-09 01:52:26 +03:00
|
|
|
analyze source
|
2022-01-11 15:59:13 +03:00
|
|
|
`errorMessageShouldBe` ( ctx (1, 35) (1, 43),
|
2022-01-09 01:52:26 +03:00
|
|
|
intercalate
|
|
|
|
"\n"
|
|
|
|
[ "Type error:",
|
2022-03-23 15:18:43 +03:00
|
|
|
" Expected type: page (declaration type)",
|
|
|
|
" Actual type: route (declaration type)",
|
|
|
|
"",
|
|
|
|
" -> For dictionary field 'to'"
|
2022-01-09 01:52:26 +03:00
|
|
|
]
|
|
|
|
)
|
2021-10-22 12:59:51 +03:00
|
|
|
|
|
|
|
it "Works when referenced declaration is declared after the reference." $ do
|
|
|
|
let source =
|
|
|
|
unlines
|
2022-01-11 15:59:13 +03:00
|
|
|
[ "route HomeRoute { path: \"/\", to: HomePage }",
|
2022-11-11 19:20:49 +03:00
|
|
|
"page HomePage { component: import Home from \"@client/HomePage.js\" }"
|
2021-10-22 12:59:51 +03:00
|
|
|
]
|
|
|
|
isRight (analyze source) `shouldBe` True
|
|
|
|
|
2022-01-09 01:50:48 +03:00
|
|
|
describe "Returns correct error message" $ do
|
|
|
|
it "For nested unexpected type error" $ do
|
|
|
|
let source =
|
|
|
|
unlines
|
|
|
|
[ "app MyApp {",
|
|
|
|
" title: \"My app\",",
|
|
|
|
" dependencies: [",
|
2022-01-11 15:59:13 +03:00
|
|
|
" (\"bar\", 13),",
|
|
|
|
" (\"foo\", 14)",
|
2022-01-09 01:50:48 +03:00
|
|
|
" ]",
|
|
|
|
"}"
|
|
|
|
]
|
|
|
|
analyze source
|
2022-01-11 15:59:13 +03:00
|
|
|
`errorMessageShouldBe` ( ctx (4, 5) (4, 15),
|
2022-01-09 01:50:48 +03:00
|
|
|
intercalate
|
|
|
|
"\n"
|
|
|
|
[ "Type error:",
|
2022-03-23 15:18:43 +03:00
|
|
|
" Expected type: (string, string)",
|
|
|
|
" Actual type: (string, number)",
|
|
|
|
"",
|
|
|
|
" -> For dictionary field 'dependencies':",
|
|
|
|
" -> In list"
|
2022-01-09 01:50:48 +03:00
|
|
|
]
|
|
|
|
)
|
|
|
|
|
|
|
|
it "For nested unification type error" $ do
|
|
|
|
let source =
|
|
|
|
unlines
|
|
|
|
[ "app MyApp {",
|
|
|
|
" title: \"My app\",",
|
|
|
|
" dependencies: [",
|
|
|
|
" { name: \"bar\", version: 13 },",
|
|
|
|
" { name: \"foo\", version: \"1.2.3\" }",
|
|
|
|
" ]",
|
|
|
|
"}"
|
|
|
|
]
|
|
|
|
analyze source
|
2022-01-09 01:55:52 +03:00
|
|
|
`errorMessageShouldBe` ( ctx (5, 29) (5, 35),
|
2022-01-09 01:50:48 +03:00
|
|
|
intercalate
|
|
|
|
"\n"
|
|
|
|
[ "Type error:",
|
2022-03-23 15:18:43 +03:00
|
|
|
" Can't mix the following types:",
|
|
|
|
" - number",
|
|
|
|
" - string",
|
|
|
|
"",
|
|
|
|
" -> For dictionary field 'version'"
|
2022-01-09 01:50:48 +03:00
|
|
|
]
|
|
|
|
)
|
|
|
|
|
|
|
|
it "For redundant dictionary field" $ do
|
|
|
|
let source =
|
|
|
|
unlines
|
|
|
|
[ "app MyApp {",
|
|
|
|
" ttle: \"My app\",",
|
|
|
|
"}"
|
|
|
|
]
|
|
|
|
analyze source
|
2022-01-09 01:55:52 +03:00
|
|
|
`errorMessageShouldBe` ( ctx (1, 11) (3, 1),
|
2022-01-09 01:50:48 +03:00
|
|
|
intercalate
|
|
|
|
"\n"
|
|
|
|
[ "Type error:",
|
|
|
|
" Unexpected dictionary field 'ttle'"
|
|
|
|
]
|
|
|
|
)
|
|
|
|
|
2021-10-22 12:59:51 +03:00
|
|
|
isAnalyzerOutputTypeError :: Either AnalyzeError a -> Bool
|
|
|
|
isAnalyzerOutputTypeError (Left (TypeError _)) = True
|
|
|
|
isAnalyzerOutputTypeError _ = False
|
2022-01-09 01:52:26 +03:00
|
|
|
|
|
|
|
errorMessageShouldBe :: Either AnalyzeError a -> (Ctx, String) -> Expectation
|
|
|
|
errorMessageShouldBe analyzeResult (c, msg) = case analyzeResult of
|
|
|
|
Right _ -> error "Test failed: expected AnalyzerError."
|
|
|
|
Left e -> getErrorMessageAndCtx e `shouldBe` (msg, c)
|