Connect to the websocket API (#35)
This commit is contained in:
parent
ff81482572
commit
583ee2def2
@ -15,8 +15,9 @@
|
||||
"elm-lang/html": "2.0.0 <= v < 3.0.0",
|
||||
"elm-lang/http": "1.0.0 <= v < 2.0.0",
|
||||
"elm-lang/navigation": "2.1.0 <= v < 3.0.0",
|
||||
"elm-lang/websocket": "1.0.2 <= v < 2.0.0",
|
||||
"evancz/url-parser": "2.0.1 <= v < 3.0.0",
|
||||
"jinjor/elm-html-parser": "1.1.4 <= v < 2.0.0",
|
||||
"jinjor/elm-html-parser": "1.1.5 <= v < 2.0.0",
|
||||
"lukewestby/elm-http-builder": "5.1.0 <= v < 6.0.0",
|
||||
"rluiten/elm-date-extra": "8.5.0 <= v < 9.0.0"
|
||||
},
|
||||
|
@ -8,7 +8,7 @@
|
||||
"debug": "node_modules/.bin/elm-live src/Main.elm --dir=public/ --output=public/app.js --debug",
|
||||
"deploy": "npm run build && node_modules/.bin/gh-pages --dist build/",
|
||||
"start": "node_modules/.bin/elm-live src/Main.elm --dir=public/ --output=public/app.js",
|
||||
"test": "node_modules/.bin/elm-make src/Main.elm --warn --output app.js"
|
||||
"test": "node_modules/.bin/elm-test"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@ -26,6 +26,7 @@
|
||||
"devDependencies": {
|
||||
"elm": "^0.18.0",
|
||||
"elm-live": "^2.7.4",
|
||||
"elm-test": "^0.18.2",
|
||||
"gh-pages": "^0.12.0"
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,7 @@ module Main exposing (..)
|
||||
|
||||
import Navigation
|
||||
import View exposing (view)
|
||||
import Model exposing (Flags, Model, Msg(..), init, update)
|
||||
import Model exposing (Flags, Model, Msg(..), init, update, subscriptions)
|
||||
|
||||
|
||||
main : Program Flags Model Msg
|
||||
@ -11,5 +11,5 @@ main =
|
||||
{ init = init
|
||||
, view = view
|
||||
, update = update
|
||||
, subscriptions = always Sub.none
|
||||
, subscriptions = subscriptions
|
||||
}
|
||||
|
170
src/Mastodon.elm
170
src/Mastodon.elm
@ -12,7 +12,9 @@ module Mastodon
|
||||
, Reblog(..)
|
||||
, Status
|
||||
, StatusRequestBody
|
||||
, StreamType(..)
|
||||
, Tag
|
||||
, WebSocketEventResult(..)
|
||||
, reblog
|
||||
, unreblog
|
||||
, favourite
|
||||
@ -22,6 +24,7 @@ module Mastodon
|
||||
, registrationEncoder
|
||||
, aggregateNotifications
|
||||
, clientEncoder
|
||||
, decodeWebSocketMessage
|
||||
, getAuthorizationUrl
|
||||
, getAccessToken
|
||||
, fetchAccount
|
||||
@ -31,6 +34,11 @@ module Mastodon
|
||||
, fetchUserTimeline
|
||||
, postStatus
|
||||
, send
|
||||
, subscribeToWebSockets
|
||||
, websocketEventDecoder
|
||||
, notificationDecoder
|
||||
, addNotificationToAggregates
|
||||
, notificationToAggregate
|
||||
)
|
||||
|
||||
import Http
|
||||
@ -38,6 +46,8 @@ import HttpBuilder
|
||||
import Json.Decode.Pipeline as Pipe
|
||||
import Json.Decode as Decode
|
||||
import Json.Encode as Encode
|
||||
import Util
|
||||
import WebSocket
|
||||
import List.Extra exposing (groupWhile)
|
||||
|
||||
|
||||
@ -82,6 +92,12 @@ type alias Client =
|
||||
}
|
||||
|
||||
|
||||
type alias WebSocketEvent =
|
||||
{ event : String
|
||||
, payload : String
|
||||
}
|
||||
|
||||
|
||||
type Error
|
||||
= MastodonError StatusCode StatusMsg String
|
||||
| ServerError StatusCode StatusMsg String
|
||||
@ -211,6 +227,18 @@ type alias Request a =
|
||||
HttpBuilder.RequestBuilder a
|
||||
|
||||
|
||||
type WebSocketEventResult a b c
|
||||
= EventError a
|
||||
| NotificationResult b
|
||||
| StatusResult c
|
||||
|
||||
|
||||
type StreamType
|
||||
= UserStream
|
||||
| LocalPublicStream
|
||||
| GlobalPublicStream
|
||||
|
||||
|
||||
|
||||
-- Msg
|
||||
|
||||
@ -367,6 +395,13 @@ statusDecoder =
|
||||
|> Pipe.required "visibility" Decode.string
|
||||
|
||||
|
||||
websocketEventDecoder : Decode.Decoder WebSocketEvent
|
||||
websocketEventDecoder =
|
||||
Pipe.decode WebSocketEvent
|
||||
|> Pipe.required "event" Decode.string
|
||||
|> Pipe.required "payload" Decode.string
|
||||
|
||||
|
||||
|
||||
-- Internal helpers
|
||||
|
||||
@ -448,6 +483,77 @@ fetch client endpoint decoder =
|
||||
-- Public API
|
||||
|
||||
|
||||
notificationToAggregate : Notification -> NotificationAggregate
|
||||
notificationToAggregate notification =
|
||||
NotificationAggregate
|
||||
notification.type_
|
||||
notification.status
|
||||
[ notification.account ]
|
||||
notification.created_at
|
||||
|
||||
|
||||
addNotificationToAggregates : Notification -> List NotificationAggregate -> List NotificationAggregate
|
||||
addNotificationToAggregates notification aggregates =
|
||||
let
|
||||
addNewAccountToSameStatus : NotificationAggregate -> Notification -> NotificationAggregate
|
||||
addNewAccountToSameStatus aggregate notification =
|
||||
case ( aggregate.status, notification.status ) of
|
||||
( Just aggregateStatus, Just notificationStatus ) ->
|
||||
if aggregateStatus.id == notificationStatus.id then
|
||||
{ aggregate | accounts = notification.account :: aggregate.accounts }
|
||||
else
|
||||
aggregate
|
||||
|
||||
( _, _ ) ->
|
||||
aggregate
|
||||
|
||||
{-
|
||||
Let's try to find an already existing aggregate, matching the notification
|
||||
we are trying to add.
|
||||
If we find any aggregate, we modify it inplace. If not, we return the
|
||||
aggregates unmodified
|
||||
-}
|
||||
newAggregates =
|
||||
aggregates
|
||||
|> List.map
|
||||
(\aggregate ->
|
||||
case ( aggregate.type_, notification.type_ ) of
|
||||
{-
|
||||
Notification and aggregate are of the follow type.
|
||||
Add the new following account.
|
||||
-}
|
||||
( "follow", "follow" ) ->
|
||||
{ aggregate | accounts = notification.account :: aggregate.accounts }
|
||||
|
||||
{-
|
||||
Notification is of type follow, but current aggregate
|
||||
is of another type. Let's continue then.
|
||||
-}
|
||||
( _, "follow" ) ->
|
||||
aggregate
|
||||
|
||||
{-
|
||||
If both types are the same check if we should
|
||||
add the new account.
|
||||
-}
|
||||
( aggregateType, notificationType ) ->
|
||||
if aggregateType == notificationType then
|
||||
addNewAccountToSameStatus aggregate notification
|
||||
else
|
||||
aggregate
|
||||
)
|
||||
in
|
||||
{-
|
||||
If we did no modification to the old aggregates it's
|
||||
because we didn't found any match. So me have to create
|
||||
a new aggregate
|
||||
-}
|
||||
if newAggregates == aggregates then
|
||||
notificationToAggregate (notification) :: aggregates
|
||||
else
|
||||
newAggregates
|
||||
|
||||
|
||||
aggregateNotifications : List Notification -> List NotificationAggregate
|
||||
aggregateNotifications notifications =
|
||||
let
|
||||
@ -600,3 +706,67 @@ unfavourite client id =
|
||||
HttpBuilder.post (client.server ++ "/api/v1/statuses/" ++ (toString id) ++ "/unfavourite")
|
||||
|> HttpBuilder.withHeader "Authorization" ("Bearer " ++ client.token)
|
||||
|> HttpBuilder.withExpect (Http.expectJson statusDecoder)
|
||||
|
||||
|
||||
subscribeToWebSockets : Client -> StreamType -> (String -> a) -> Sub a
|
||||
subscribeToWebSockets client streamType message =
|
||||
let
|
||||
type_ =
|
||||
case streamType of
|
||||
UserStream ->
|
||||
"user"
|
||||
|
||||
LocalPublicStream ->
|
||||
"public:local"
|
||||
|
||||
GlobalPublicStream ->
|
||||
"public:local"
|
||||
|
||||
url =
|
||||
(Util.replace "https" "wss" client.server)
|
||||
++ "/api/v1/streaming/?access_token="
|
||||
++ client.token
|
||||
++ "&stream="
|
||||
++ type_
|
||||
in
|
||||
WebSocket.listen url message
|
||||
|
||||
|
||||
|
||||
{-
|
||||
Sorry for this beast, but the websocket connection return messages
|
||||
containing an escaped JSON string under the `payload` key. This JSON string
|
||||
can either represent a `Notification` when the event field of the returned json
|
||||
is equal to 'notification' or a `Status` when the string is equal to
|
||||
'update'.
|
||||
If someone has a better way of doing this, I'me all for it
|
||||
-}
|
||||
|
||||
|
||||
decodeWebSocketMessage : String -> WebSocketEventResult String (Result String Notification) (Result String Status)
|
||||
decodeWebSocketMessage message =
|
||||
let
|
||||
websocketEvent =
|
||||
Decode.decodeString
|
||||
websocketEventDecoder
|
||||
message
|
||||
in
|
||||
case websocketEvent of
|
||||
Ok event ->
|
||||
if event.event == "notification" then
|
||||
NotificationResult
|
||||
(Decode.decodeString
|
||||
notificationDecoder
|
||||
event.payload
|
||||
)
|
||||
else if event.event == "update" then
|
||||
StatusResult
|
||||
(Decode.decodeString
|
||||
statusDecoder
|
||||
event.payload
|
||||
)
|
||||
else
|
||||
EventError "Unknown event type for WebSocket"
|
||||
|
||||
Err error ->
|
||||
EventError error
|
||||
|
@ -61,6 +61,9 @@ type
|
||||
| Unreblog Int
|
||||
| Unreblogged (Result Mastodon.Error Mastodon.Status)
|
||||
| UserTimeline (Result Mastodon.Error (List Mastodon.Status))
|
||||
| NewWebsocketUserMessage String
|
||||
| NewWebsocketGlobalMessage String
|
||||
| NewWebsocketLocalMessage String
|
||||
| ViewerEvent ViewerMsg
|
||||
|
||||
|
||||
@ -535,3 +538,60 @@ update msg model =
|
||||
|
||||
Err error ->
|
||||
{ model | notifications = [], errors = (errorText error) :: model.errors } ! []
|
||||
|
||||
NewWebsocketUserMessage message ->
|
||||
case (Mastodon.decodeWebSocketMessage message) of
|
||||
Mastodon.EventError error ->
|
||||
{ model | errors = error :: model.errors } ! []
|
||||
|
||||
Mastodon.NotificationResult result ->
|
||||
case result of
|
||||
Ok notification ->
|
||||
{ model | notifications = Mastodon.addNotificationToAggregates notification model.notifications } ! []
|
||||
|
||||
Err error ->
|
||||
{ model | errors = error :: model.errors } ! []
|
||||
|
||||
Mastodon.StatusResult result ->
|
||||
case result of
|
||||
Ok status ->
|
||||
{ model | userTimeline = status :: model.userTimeline } ! []
|
||||
|
||||
Err error ->
|
||||
{ model | errors = error :: model.errors } ! []
|
||||
|
||||
NewWebsocketLocalMessage message ->
|
||||
-- @TODO
|
||||
model ! []
|
||||
|
||||
NewWebsocketGlobalMessage message ->
|
||||
-- @TODO
|
||||
model ! []
|
||||
|
||||
|
||||
subscriptions : Model -> Sub Msg
|
||||
subscriptions model =
|
||||
Sub.batch <|
|
||||
case model.client of
|
||||
Just client ->
|
||||
[ Mastodon.subscribeToWebSockets
|
||||
client
|
||||
Mastodon.UserStream
|
||||
NewWebsocketUserMessage
|
||||
]
|
||||
++ (if model.useGlobalTimeline then
|
||||
[ Mastodon.subscribeToWebSockets
|
||||
client
|
||||
Mastodon.GlobalPublicStream
|
||||
NewWebsocketGlobalMessage
|
||||
]
|
||||
else
|
||||
[ Mastodon.subscribeToWebSockets
|
||||
client
|
||||
Mastodon.LocalPublicStream
|
||||
NewWebsocketLocalMessage
|
||||
]
|
||||
)
|
||||
|
||||
Nothing ->
|
||||
[]
|
||||
|
6
src/Util.elm
Normal file
6
src/Util.elm
Normal file
@ -0,0 +1,6 @@
|
||||
module Util exposing (..)
|
||||
|
||||
|
||||
replace : String -> String -> String -> String
|
||||
replace from to str =
|
||||
String.split from str |> String.join to
|
@ -3,7 +3,6 @@ module ViewHelper
|
||||
( formatContent
|
||||
, getMentionForLink
|
||||
, onClickWithPreventAndStop
|
||||
, replace
|
||||
, toVirtualDom
|
||||
)
|
||||
|
||||
@ -14,6 +13,7 @@ import HtmlParser
|
||||
import Json.Decode as Decode
|
||||
import Mastodon
|
||||
import Model exposing (Msg(OnLoadUserAccount))
|
||||
import Util
|
||||
|
||||
|
||||
-- Custom Events
|
||||
@ -34,18 +34,13 @@ onClickWithPreventAndStop msg =
|
||||
formatContent : String -> List Mastodon.Mention -> List (Html Msg)
|
||||
formatContent content mentions =
|
||||
content
|
||||
|> replace " ?" " ?"
|
||||
|> replace " !" " !"
|
||||
|> replace " :" " :"
|
||||
|> Util.replace " ?" " ?"
|
||||
|> Util.replace " !" " !"
|
||||
|> Util.replace " :" " :"
|
||||
|> HtmlParser.parse
|
||||
|> toVirtualDom mentions
|
||||
|
||||
|
||||
replace : String -> String -> String -> String
|
||||
replace from to str =
|
||||
String.split from str |> String.join to
|
||||
|
||||
|
||||
{-| Converts nodes to virtual dom nodes.
|
||||
-}
|
||||
toVirtualDom : List Mastodon.Mention -> List HtmlParser.Node -> List (Html Msg)
|
||||
|
1
tests/.gitignore
vendored
Normal file
1
tests/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
/elm-stuff/
|
208
tests/Fixtures.elm
Normal file
208
tests/Fixtures.elm
Normal file
@ -0,0 +1,208 @@
|
||||
module Fixtures exposing (..)
|
||||
|
||||
import Mastodon exposing (Account, Notification, NotificationAggregate, Status)
|
||||
|
||||
|
||||
accountSkro : Account
|
||||
accountSkro =
|
||||
{ acct = "SkroZoC"
|
||||
, avatar = "https://mamot.fr/system/accounts/avatars/000/001/391/original/76be3c9d1b34f59b.jpeg?1493042489"
|
||||
, created_at = "2017-04-24T20:25:37.398Z"
|
||||
, display_name = "Skro"
|
||||
, followers_count = 77
|
||||
, following_count = 80
|
||||
, header = "https://mamot.fr/system/accounts/headers/000/001/391/original/9fbb4ac980f04fe1.gif?1493042489"
|
||||
, id = 1391
|
||||
, locked = False
|
||||
, note = "N'importe quoi très vite en 500 caractères. La responsabilité du triumvirat de ZoC ne peut être engagée."
|
||||
, statuses_count = 161
|
||||
, url = "https://mamot.fr/@SkroZoC"
|
||||
, username = "SkroZoC"
|
||||
}
|
||||
|
||||
|
||||
accountVjousse : Account
|
||||
accountVjousse =
|
||||
{ acct = "vjousse"
|
||||
, avatar = "https://mamot.fr/system/accounts/avatars/000/026/303/original/b72c0dd565e5bc1e.png?1492698808"
|
||||
, created_at = "2017-04-20T14:31:05.751Z"
|
||||
, display_name = "Vincent Jousse"
|
||||
, followers_count = 68
|
||||
, following_count = 31
|
||||
, header = "https://mamot.fr/headers/original/missing.png"
|
||||
, id = 26303
|
||||
, locked = False
|
||||
, note = "Libriste, optimiste et utopiste. On est bien tintin."
|
||||
, statuses_count = 88
|
||||
, url = "https://mamot.fr/@vjousse"
|
||||
, username = "vjousse"
|
||||
}
|
||||
|
||||
|
||||
accountNico : Account
|
||||
accountNico =
|
||||
{ acct = "n1k0"
|
||||
, avatar = "https://mamot.fr/system/accounts/avatars/000/017/784/original/40052904e484d9c0.jpg?1492158615"
|
||||
, created_at = "2017-04-14T08:28:59.706Z"
|
||||
, display_name = "NiKo`"
|
||||
, followers_count = 162
|
||||
, following_count = 79
|
||||
, header = "https://mamot.fr/system/accounts/headers/000/017/784/original/ea87200d852018a8.jpg?1492158674"
|
||||
, id = 17784
|
||||
, locked = False
|
||||
, note = "Transforme sa procrastination en pouets, la plupart du temps en français."
|
||||
, statuses_count = 358
|
||||
, url = "https://mamot.fr/@n1k0"
|
||||
, username = "n1k0"
|
||||
}
|
||||
|
||||
|
||||
accountPloum : Account
|
||||
accountPloum =
|
||||
{ acct = "ploum"
|
||||
, avatar = "https://mamot.fr/system/accounts/avatars/000/006/840/original/593a817d651d9253.jpg?1491814416"
|
||||
, created_at = "2017-04-08T09:37:34.931Z"
|
||||
, display_name = "ploum"
|
||||
, followers_count = 1129
|
||||
, following_count = 91
|
||||
, header = "https://mamot.fr/system/accounts/headers/000/006/840/original/7e0adc1f754dafbe.jpg?1491814416"
|
||||
, id = 6840
|
||||
, locked = False
|
||||
, note = "Futurologue, conférencier, blogueur et écrivain électronique. Du moins, je l'espère. :bicyclist:"
|
||||
, statuses_count = 601
|
||||
, url = "https://mamot.fr/@ploum"
|
||||
, username = "ploum"
|
||||
}
|
||||
|
||||
|
||||
statusNicoToVjousse : Status
|
||||
statusNicoToVjousse =
|
||||
{ account = accountNico
|
||||
, content = "<p><span class=\"h-card\"><a href=\"https://mamot.fr/@vjousse\" class=\"u-url mention\">@<span>vjousse</span></a></span> j'ai rien touché à ce niveau là non</p>"
|
||||
, created_at = "2017-04-24T20:16:20.922Z"
|
||||
, favourited = Nothing
|
||||
, favourites_count = 0
|
||||
, id = 737932
|
||||
, in_reply_to_account_id = Just 26303
|
||||
, in_reply_to_id = Just 737425
|
||||
, media_attachments = []
|
||||
, mentions =
|
||||
[ { id = 26303
|
||||
, url = "https://mamot.fr/@vjousse"
|
||||
, username = "vjousse"
|
||||
, acct = "vjousse"
|
||||
}
|
||||
]
|
||||
, reblog = Nothing
|
||||
, reblogged = Nothing
|
||||
, reblogs_count = 0
|
||||
, sensitive = Just False
|
||||
, spoiler_text = ""
|
||||
, tags = []
|
||||
, uri = "tag:mamot.fr,2017-04-24:objectId=737932:objectType=Status"
|
||||
, url = "https://mamot.fr/@n1k0/737932"
|
||||
, visibility = "public"
|
||||
}
|
||||
|
||||
|
||||
statusNicoToVjousseAgain : Status
|
||||
statusNicoToVjousseAgain =
|
||||
{ account = accountNico
|
||||
, content = "<p><span class=\"h-card\"><a href=\"https://mamot.fr/@vjousse\" class=\"u-url mention\">@<span>vjousse</span></a></span> oui j'ai vu, c'est super, après on est à +473 −13, à un moment tu vas te prendre la tête 😂</p>"
|
||||
, created_at = "2017-04-25T07:41:23.492Z"
|
||||
, favourited = Nothing
|
||||
, favourites_count = 0
|
||||
, id = 752169
|
||||
, in_reply_to_account_id = Just 26303
|
||||
, in_reply_to_id = Just 752153
|
||||
, media_attachments = []
|
||||
, mentions =
|
||||
[ { id = 26303
|
||||
, url = "https://mamot.fr/@vjousse"
|
||||
, username = "vjousse"
|
||||
, acct = "vjousse"
|
||||
}
|
||||
]
|
||||
, reblog = Nothing
|
||||
, reblogged = Nothing
|
||||
, reblogs_count = 0
|
||||
, sensitive = Just False
|
||||
, spoiler_text = ""
|
||||
, tags = []
|
||||
, uri = "tag:mamot.fr,2017-04-25:objectId=752169:objectType=Status"
|
||||
, url = "https://mamot.fr/@n1k0/752169"
|
||||
, visibility = "public"
|
||||
}
|
||||
|
||||
|
||||
notificationNicoMentionVjousse : Notification
|
||||
notificationNicoMentionVjousse =
|
||||
{ id = 224284
|
||||
, type_ = "mention"
|
||||
, created_at = "2017-04-24T20:16:20.973Z"
|
||||
, account = accountNico
|
||||
, status = Just statusNicoToVjousse
|
||||
}
|
||||
|
||||
|
||||
notificationNicoMentionVjousseAgain : Notification
|
||||
notificationNicoMentionVjousseAgain =
|
||||
{ id = 226516
|
||||
, type_ = "mention"
|
||||
, created_at = "2017-04-25T07:41:23.546Z"
|
||||
, account = accountNico
|
||||
, status = Just statusNicoToVjousseAgain
|
||||
}
|
||||
|
||||
|
||||
notificationNicoFollowsVjousse : Notification
|
||||
notificationNicoFollowsVjousse =
|
||||
{ id = 224257
|
||||
, type_ = "follow"
|
||||
, created_at = "2017-04-24T20:13:47.431Z"
|
||||
, account = accountNico
|
||||
, status = Nothing
|
||||
}
|
||||
|
||||
|
||||
notificationSkroFollowsVjousse : Notification
|
||||
notificationSkroFollowsVjousse =
|
||||
{ id = 224
|
||||
, type_ = "follow"
|
||||
, created_at = "2017-04-24T19:12:47.431Z"
|
||||
, account = accountSkro
|
||||
, status = Nothing
|
||||
}
|
||||
|
||||
|
||||
notificationPloumFollowsVjousse : Notification
|
||||
notificationPloumFollowsVjousse =
|
||||
{ id = 220
|
||||
, type_ = "follow"
|
||||
, created_at = "2017-04-24T18:12:47.431Z"
|
||||
, account = accountPloum
|
||||
, status = Nothing
|
||||
}
|
||||
|
||||
|
||||
accounts : List Account
|
||||
accounts =
|
||||
[ accountSkro, accountVjousse, accountNico ]
|
||||
|
||||
|
||||
notifications : List Notification
|
||||
notifications =
|
||||
[ notificationNicoMentionVjousse
|
||||
, notificationNicoFollowsVjousse
|
||||
, notificationSkroFollowsVjousse
|
||||
]
|
||||
|
||||
|
||||
notificationAggregates : List NotificationAggregate
|
||||
notificationAggregates =
|
||||
[ { type_ = "mention"
|
||||
, status = Nothing
|
||||
, accounts = []
|
||||
, created_at = ""
|
||||
}
|
||||
]
|
13
tests/Main.elm
Normal file
13
tests/Main.elm
Normal file
@ -0,0 +1,13 @@
|
||||
port module Main exposing (..)
|
||||
|
||||
import NotificationTests
|
||||
import Test.Runner.Node exposing (run, TestProgram)
|
||||
import Json.Encode exposing (Value)
|
||||
|
||||
|
||||
main : TestProgram
|
||||
main =
|
||||
run emit NotificationTests.all
|
||||
|
||||
|
||||
port emit : ( String, Value ) -> Cmd msg
|
87
tests/NotificationTests.elm
Normal file
87
tests/NotificationTests.elm
Normal file
@ -0,0 +1,87 @@
|
||||
module NotificationTests exposing (..)
|
||||
|
||||
import Test exposing (..)
|
||||
import Expect
|
||||
import String
|
||||
import Mastodon
|
||||
import Fixtures
|
||||
|
||||
|
||||
all : Test
|
||||
all =
|
||||
describe "Notification test suite"
|
||||
[ describe "Aggegate test"
|
||||
[ test "Aggregate Notifications" <|
|
||||
\() ->
|
||||
Fixtures.notifications
|
||||
|> Mastodon.aggregateNotifications
|
||||
|> Expect.equal
|
||||
[ { type_ = "mention"
|
||||
, status = Just Fixtures.statusNicoToVjousse
|
||||
, accounts = [ Fixtures.accountNico ]
|
||||
, created_at = "2017-04-24T20:16:20.973Z"
|
||||
}
|
||||
, { type_ = "follow"
|
||||
, status = Nothing
|
||||
, accounts = [ Fixtures.accountNico, Fixtures.accountSkro ]
|
||||
, created_at = "2017-04-24T20:13:47.431Z"
|
||||
}
|
||||
]
|
||||
, test "Add follows notification to aggregate" <|
|
||||
\() ->
|
||||
Fixtures.notifications
|
||||
|> Mastodon.aggregateNotifications
|
||||
|> (Mastodon.addNotificationToAggregates Fixtures.notificationPloumFollowsVjousse)
|
||||
|> Expect.equal
|
||||
[ { type_ = "mention"
|
||||
, status = Just Fixtures.statusNicoToVjousse
|
||||
, accounts = [ Fixtures.accountNico ]
|
||||
, created_at = "2017-04-24T20:16:20.973Z"
|
||||
}
|
||||
, { type_ = "follow"
|
||||
, status = Nothing
|
||||
, accounts = [ Fixtures.accountPloum, Fixtures.accountNico, Fixtures.accountSkro ]
|
||||
, created_at = "2017-04-24T20:13:47.431Z"
|
||||
}
|
||||
]
|
||||
, test "Add mention notification to aggregate" <|
|
||||
\() ->
|
||||
Fixtures.notifications
|
||||
|> Mastodon.aggregateNotifications
|
||||
|> (Mastodon.addNotificationToAggregates Fixtures.notificationNicoMentionVjousse)
|
||||
|> Expect.equal
|
||||
[ { type_ = "mention"
|
||||
, status = Just Fixtures.statusNicoToVjousse
|
||||
, accounts = [ Fixtures.accountNico, Fixtures.accountNico ]
|
||||
, created_at = "2017-04-24T20:16:20.973Z"
|
||||
}
|
||||
, { type_ = "follow"
|
||||
, status = Nothing
|
||||
, accounts = [ Fixtures.accountNico, Fixtures.accountSkro ]
|
||||
, created_at = "2017-04-24T20:13:47.431Z"
|
||||
}
|
||||
]
|
||||
, test "Add new mention notification to aggregate" <|
|
||||
\() ->
|
||||
Fixtures.notifications
|
||||
|> Mastodon.aggregateNotifications
|
||||
|> (Mastodon.addNotificationToAggregates Fixtures.notificationNicoMentionVjousseAgain)
|
||||
|> Expect.equal
|
||||
[ { type_ = "mention"
|
||||
, status = Just Fixtures.statusNicoToVjousseAgain
|
||||
, accounts = [ Fixtures.accountNico ]
|
||||
, created_at = "2017-04-25T07:41:23.546Z"
|
||||
}
|
||||
, { type_ = "mention"
|
||||
, status = Just Fixtures.statusNicoToVjousse
|
||||
, accounts = [ Fixtures.accountNico ]
|
||||
, created_at = "2017-04-24T20:16:20.973Z"
|
||||
}
|
||||
, { type_ = "follow"
|
||||
, status = Nothing
|
||||
, accounts = [ Fixtures.accountNico, Fixtures.accountSkro ]
|
||||
, created_at = "2017-04-24T20:13:47.431Z"
|
||||
}
|
||||
]
|
||||
]
|
||||
]
|
32
tests/elm-package.json
Normal file
32
tests/elm-package.json
Normal file
@ -0,0 +1,32 @@
|
||||
{
|
||||
"version": "1.0.0",
|
||||
"summary": "Sample Elm Test",
|
||||
"repository": "https://github.com/user/project.git",
|
||||
"license": "BSD-3-Clause",
|
||||
"source-directories": [
|
||||
".",
|
||||
"../src"
|
||||
],
|
||||
"exposed-modules": [],
|
||||
"dependencies": {
|
||||
"elm-community/json-extra": "2.0.0 <= v < 3.0.0",
|
||||
"elm-lang/html": "2.0.0 <= v < 3.0.0",
|
||||
"mgold/elm-random-pcg": "4.0.2 <= v < 5.0.0",
|
||||
"elm-lang/core": "5.0.0 <= v < 6.0.0",
|
||||
"elm-community/elm-test": "3.0.0 <= v < 4.0.0",
|
||||
"rtfeldman/node-test-runner": "3.0.0 <= v < 4.0.0",
|
||||
|
||||
"NoRedInk/elm-decode-pipeline": "3.0.0 <= v < 4.0.0",
|
||||
"elm-community/list-extra": "6.0.0 <= v < 7.0.0",
|
||||
"elm-lang/core": "5.1.1 <= v < 6.0.0",
|
||||
"elm-lang/dom": "1.1.1 <= v < 2.0.0",
|
||||
"elm-lang/html": "2.0.0 <= v < 3.0.0",
|
||||
"elm-lang/http": "1.0.0 <= v < 2.0.0",
|
||||
"elm-lang/navigation": "2.1.0 <= v < 3.0.0",
|
||||
"elm-lang/websocket": "1.0.2 <= v < 2.0.0",
|
||||
"evancz/url-parser": "2.0.1 <= v < 3.0.0",
|
||||
"jinjor/elm-html-parser": "1.1.5 <= v < 2.0.0",
|
||||
"lukewestby/elm-http-builder": "5.1.0 <= v < 6.0.0"
|
||||
},
|
||||
"elm-version": "0.18.0 <= v < 0.19.0"
|
||||
}
|
Loading…
Reference in New Issue
Block a user