crm_sdk/testing.dart
The fake server and client for your tests and demo builds.
import 'package:crm_sdk/testing.dart';| Name | What it is |
|---|---|
FakeCrmClient | PurrCRM, in memory: its guest routes, at the root (/guest/v1/..., as on PurrCRM’s own host) or under /crm (a CRM whose routes are under a path prefix), and the storage the messenger’s pictures go to, all answered by server, on whatever host the request names. |
FakeCrmHost | The platform around the fake PurrCRM. |
FakeCrmNotice | Something PurrCRM tells a guest that they have not seen: a reply in a conversation nobody was waiting on, which the platform keeps as a notice and pushes to the guest’s phones. |
FakeCrmRequest | One request to the fake, as a client hands it on. |
FakeCrmResponse | What the fake answers: a status, and a JSON body or none. |
FakeCrmServer | The fake PurrCRM. |
FakeCrmTest | One test staff sent to a test phone, and what the guest did with it. |
StandaloneCrmHost | A host of the fake’s own, for a FakeCrmServer with no product’s fake around it: a package’s tests, or a demo build of a product. |
crmFixtureDecoders | Every fixture a model of the package’s reads, by file name, with the model’s decoder. |
fakeMessengerLayout | A home the app’s staff published, for a demo and a test of the fake (FakeCrmServer.messengerLayout): a greeting with the guest’s name, Alex and Sam’s faces with their names, an away message, a status, the send card, a link into the product, the help center’s search and its featured articles, then the guest’s conversations and tickets. |
fakeNewsTopic | The fake’s News topic’s id. |
fakeOffersTopic | The fake’s Offers topic’s id. |
FakeCrmClient
Section titled “FakeCrmClient”class FakeCrmClient extends BaseClientPurrCRM, in memory: its guest routes, at the root (/guest/v1/..., as
on PurrCRM’s own host) or under /crm (a CRM whose routes are under a
path prefix),
and the storage the messenger’s pictures go to, all answered by
server, on whatever host the request names. Hand it to the facade as
CrmConfig.httpClient, with any apiBase whose path is empty (base,
say) or /crm:
final server = FakeCrmServer();final PurrCRM = Crm( config: CrmConfig( apiBase: FakeCrmClient.base, appVersion: '1.0.0', productName: 'Acme', httpClient: FakeCrmClient(server), ), auth: CrmAuth.tokens(myTokens),);The guest is whoever the bearer token names to the server’s host: with a StandaloneCrmHost, the token itself.
Members of FakeCrmClient
Section titled “Members of FakeCrmClient”FakeCrmClient()
Section titled “FakeCrmClient()”FakeCrmClient(FakeCrmServer server, {Duration latency = Duration.zero})base (static)
Section titled “base (static)”static final Uri baseWhere a facade over the fake can say the platform is. Nothing is ever sent there: the client answers every request itself.
latency
Section titled “latency”Duration latencyHow long every answer takes. A demo build sets a little, so that the screens’ waiting states can be seen.
offline
Section titled “offline”bool offlineEvery request fails as a network failure would, as on a phone with no connection.
requests
Section titled “requests”final List<FakeCrmRequest> requestsEvery request PurrCRM was sent, oldest first: a wait once it has been let go.
server
Section titled “server”final FakeCrmServer servervoid close()Answers nothing more. The server goes on: close it too (FakeCrmServer.close) to let go of the waits it holds.
FakeCrmHost
Section titled “FakeCrmHost”abstract interface class FakeCrmHostThe platform around the fake PurrCRM.
Members of FakeCrmHost
Section titled “Members of FakeCrmHost”DateTime get nowWhat time it is, which every answer is written in.
guestFor
Section titled “guestFor”String? guestFor(String token)The guest the bearer token is for; null for a token nobody holds,
or one that has run out, which PurrCRM answers with a 401 token_expired.
bool isOn(CrmFeature feature)Whether the app has feature on: PurrCRM answers a route
of a part that is off with a 404.
joinedAt
Section titled “joinedAt”DateTime joinedAt(String guestId)When guestId signed up: every guest of the fake is a test phone,
added then.
productEventsOff
Section titled “productEventsOff”bool productEventsOff(String guestId)Whether guestId turned the product events off (“Help improve …”):
PurrCRM keeps none of theirs.
void tell(String guestId, FakeCrmNotice notice)PurrCRM telling guestId of notice, as PurrCRM’s notices and
pushes would.
FakeCrmNotice
Section titled “FakeCrmNotice”final class FakeCrmNoticeSomething PurrCRM tells a guest that they have not seen: a reply in a conversation nobody was waiting on, which the platform keeps as a notice and pushes to the guest’s phones.
Members of FakeCrmNotice
Section titled “Members of FakeCrmNotice”FakeCrmNotice()
Section titled “FakeCrmNotice()”const FakeCrmNotice({required CrmPushType type, required String title, String body = '', String link = ''})final String bodyfinal String linkPurrCRM’s page it opens (/messages/<id>), as PurrCRM writes its links.
final String titleThe line a notification shows (“Alex replied”), and the words under it.
final CrmPushType typeWhat kind of push it is: a support_reply, for a reply.
FakeCrmRequest
Section titled “FakeCrmRequest”final class FakeCrmRequestOne request to the fake, as a client hands it on.
Members of FakeCrmRequest
Section titled “Members of FakeCrmRequest”FakeCrmRequest()
Section titled “FakeCrmRequest()”const FakeCrmRequest({required String method, required List<String> path, Map<String, String> query = const {}, Map<String, String> headers = const {}, String body = ''})final String bodyThe body as text; empty when there was none.
headers
Section titled “headers”final Map<String, String> headersThe headers, with lower-case names.
method
Section titled “method”final String methodfinal List<String> pathThe path within PurrCRM, a segment each: ['guest', 'v1', 'checkin'] for
what PurrCRM serves as /guest/v1/checkin, whatever prefix the
client found it under.
final Map<String, String> queryisWait
Section titled “isWait”bool get isWaitWhether this is a conversation’s wait, which PurrCRM holds until something
arrives (FakeCrmServer.hold).
FakeCrmResponse
Section titled “FakeCrmResponse”final class FakeCrmResponseWhat the fake answers: a status, and a JSON body or none.
Members of FakeCrmResponse
Section titled “Members of FakeCrmResponse”FakeCrmResponse()
Section titled “FakeCrmResponse()”const FakeCrmResponse(int status, [Object? json, Map<String, String> headers = const {}, bool problem = false])headers
Section titled “headers”final Map<String, String> headersHeaders beyond the content type.
final Object? jsonThe body, to be written as JSON; null for none.
problem
Section titled “problem”final bool problemWhether the body is a problem (application/problem+json).
status
Section titled “status”final int statusallHeaders
Section titled “allHeaders”Map<String, String> get allHeadersEvery header to answer with: the content type, when there is a body,
and headers.
List<int> get bytesThe body as it goes on the wire.
FakeCrmServer
Section titled “FakeCrmServer”class FakeCrmServerThe fake PurrCRM. See the library’s comment.
Members of FakeCrmServer
Section titled “Members of FakeCrmServer”FakeCrmServer()
Section titled “FakeCrmServer()”FakeCrmServer({FakeCrmHost? host, Uri? storageUrl, String productName = 'Acme', String appKey = defaultAppKey, Map<String, Object?>? orderRef = defaultOrderRef, bool unverified = false, Set<String> customEvents = const {}})A server over host (a StandaloneCrmHost when not given) whose
pictures go to storageUrl, which names the product productName,
is the app appKey and knows the product’s own events
customEvents beside the package’s.
appKey
Section titled “appKey”final String appKeyThe key of the app the fake is (CrmConfig.appKey), defaultAppKey
unless told. Any other key is refused, and so is none, so that a
product that forgot its key finds out. A test key (app_test_…)
makes the fake a test app: its public face says mode test, which
Crm.isTestMode reads.
customEvents
Section titled “customEvents”final Set<String> customEventsThe product’s own events, by their words, which PurrCRM takes beside the package’s (CrmEvent).
defaultAppKey (static)
Section titled “defaultAppKey (static)”static const String defaultAppKeyThe key the fake is unless told.
defaultOrderRef (static)
Section titled “defaultOrderRef (static)”static const Map<String, Object?> defaultOrderRefThe order reference the fake’s app has unless told.
defaultStorageUrl (static)
Section titled “defaultStorageUrl (static)”static final Uri defaultStorageUrlWhere pictures go unless the server is told otherwise.
emailClicks
Section titled “emailClicks”final List<String> emailClicksThe email clicks the app counted itself (POST /public/v1/email/c/{token}), by their tokens, oldest first.
events
Section titled “events”final List<EventBody> eventsEvery product event PurrCRM kept, oldest first: each batch’s events from a guest who had not turned them off, once each by id.
helpBot
Section titled “helpBot”bool helpBotWhether a new conversation is met by the help bot; off, Alex answers every message.
linkBase
Section titled “linkBase”String linkBaseWhere the app’s links are, as its public face says: empty for an app with no link domain, the fake’s own unless told.
linkMessageId (static)
Section titled “linkMessageId (static)”static const String linkMessageIdThe post only its link opens (/messages/show/<id>): never in a
check-in, shown by Crm.showMessage.
linkReports
Section titled “linkReports”final List<AppLinksReport> linkReportsThe links the app’s builds declared (PUT /guest/v1/app-links), oldest first.
messengerLayout
Section titled “messengerLayout”Map<String, Object?>? messengerLayoutThe messenger’s home as the app’s staff published it, sent to every guest as it is, but for its send
card while the guest may not start a conversation; null for none,
which is a home as it always was (fakeMessengerLayout is one). Its
options are held as PurrCRM holds them: a start under never, or under
when_none_open while one is open, is 409 refused (and the home has no
opening), and a picture with attachments off is 422
attachment_invalid.
messengerWaitFor
Section titled “messengerWaitFor”Duration messengerWaitForHow long a conversation’s wait is held with nothing new before it answers with nothing, where PurrCRM holds it for messengerWait: short, so that a demo build’s conversation comes round often.
orderRef
Section titled “orderRef”final Map<String, Object?>? orderRefThe app’s order reference, which the help bot’s question about an order carries, as JSON; null for an app without one.
productName
Section titled “productName”final String productNameWhat the help bot, the survey and the welcome post call the product.
staffReplyAfter
Section titled “staffReplyAfter”Duration? staffReplyAfterHow long after the guest writes Alex answers; null for never, which a
test that answers by itself (staffReply) wants.
storageUrl
Section titled “storageUrl”final Uri storageUrlWhere PurrCRM’s object store takes the messenger’s pictures: a
picture’s upload names it, and a client posts the form there
(store).
unverified
Section titled “unverified”final bool unverifiedWhether the fake’s app takes unverified guests: a request with no Authorization that names its guest in X-CRM-User-Id is that guest’s, as the host’s guestFor reads the id. Off, such a request is refused, as PurrCRM refuses it.
articleRatings
Section titled “articleRatings”Map<(String, String), bool> get articleRatingsWhat each guest said about each help article, by guest id and slug.
closed
Section titled “closed”bool get closedWhether close has been called.
FakeCrmHost get hostThe platform around it.
DateTime get nowThe host’s time, in UTC.
answer
Section titled “answer”FakeCrmResponse? answer(FakeCrmRequest request)What PurrCRM answers request with, or null for a path it does not
serve.
void close()Answers every wait and stops every timer: nothing of the server’s is left running.
closeConversation
Section titled “closeConversation”void closeConversation(String id)Staff closing the conversation id, which asks the guest to rate it.
contentOf
Section titled “contentOf”Map<String, Object?> contentOf(MessageKind kind, {String? look})The fake’s own content of its message of kind, as PurrCRM sends it: to
send a test of it changed (sendTest’s content). The fake has two
banners and two surveys: look picks one by its banner style
(floating, inline) or survey format (small, large); without
it the first, the floating banner and the small survey.
conversationsOf
Section titled “conversationsOf”List<String> conversationsOf(String guestId)The ids of guestId’s conversations, the most recently changed
first: on a guest’s first look, the three every guest finds (one
open with an unread reply from Alex, one closed asking for a rating,
and one about the ticket in progress).
devicesOf
Section titled “devicesOf”Map<String, CrmDeviceBody> devicesOf(String guestId)The devices guestId registered for PurrCRM’s pushes, by token.
exportOf
Section titled “exportOf”Map<String, Object?>? exportOf(String guestId)PurrCRM’s part of guestId’s export, as a host’s export carries it:
their conversations and tickets, and with in-app messages on, their
test phone, tests, receipts and survey answers. Null while neither
the messenger nor in-app messages is on, when PurrCRM is not asked.
forget
Section titled “forget”void forget(String guestId)Everything of guestId’s, for an account deleted.
handle
Section titled “handle”Future<FakeCrmResponse?> handle(FakeCrmRequest request)request, answered: held first while it is a wait.
Future<void> hold(FakeCrmRequest request)Holds a conversation’s wait until a part arrives or messengerWaitFor
runs out, as PurrCRM holds it; at once when there is one already, when
the server is closed, or when the wait is not the guest’s own (the
route then refuses it). Anything but a wait is not held.
notServed (static)
Section titled “notServed (static)”static FakeCrmResponse notServed(String path)What PurrCRM answers a path it does not serve with.
notice
Section titled “notice”String notice(String guestId, {CrmPushType type = CrmPushType.crmMessage, required String title, required String body, String link = ''})A notice in guestId’s inbox, as PurrCRM keeps one with each push it
sends for an app that delivers its own: its id. The fake pushes
nothing; a test hands the push to the facade itself.
offersByPushOf
Section titled “offersByPushOf”bool offersByPushOf(String guestId)guestId’s consent to offers by push, as PurrCRM keeps it.
preferencesOf
Section titled “preferencesOf”CrmPreferences preferencesOf(String guestId)guestId’s preferences, as PurrCRM keeps them: the fake’s
two topics, Offers (fakeOffersTopic: email, push and in the app,
on by default but for email) and News (fakeNewsTopic: push and
chat, off by default).
resetMessages
Section titled “resetMessages”void resetMessages(String guestId)Forgets what guestId did with the fake’s in-app messages (seen,
dismissed, answered) and when they last checked in, so that the next
check-in hands every one again, as on their first: for a test bench
such as the example’s Message lab. Their conversations, notices and
the tests staff sent stay. PurrCRM itself never forgets this way.
sendTest
Section titled “sendTest”FakeCrmTest sendTest(MessageKind kind, {Map<String, Object?>? content})Staff sending a test of the fake’s message of kind (its banner,
survey, post or carousel) to a test phone: the next guest who checks
in is handed it first. content in place of the fake’s own (a
carousel of another presentation, say), as PurrCRM sends the kind’s
content. The test, to see what became of it.
staffReply
Section titled “staffReply”void staffReply(String id, String text, {String name = 'Alex'})A reply from a member of staff (name) in the conversation id, as
PurrCRM’s inbox would send one: it wakes the conversation’s waits, and is
a support_reply notice to the guest (FakeCrmHost.tell) when nobody is
waiting on it. As PurrCRM’s does, it opens a closed conversation again,
with a line saying so by the one replying, and wakes a snoozed one.
FakeCrmResponse store(String body)A form posted to the storage (storageUrl): the picture its key
names is uploaded.
FakeCrmTest
Section titled “FakeCrmTest”final class FakeCrmTestOne test staff sent to a test phone, and what the guest did with it.
Members of FakeCrmTest
Section titled “Members of FakeCrmTest”answers
Section titled “answers”final Map<String, SurveyAnswer> answersA survey test’s answers, by question.
clicked
Section titled “clicked”bool clickedcompleted
Section titled “completed”bool completeddismissed
Section titled “dismissed”bool dismissedguestId
Section titled “guestId”String? guestIdThe guest it was handed to; null until a check-in took it.
final String idfinal MessageKind kindbool seensentAt
Section titled “sentAt”final DateTime sentAtWhen staff sent it.
StandaloneCrmHost
Section titled “StandaloneCrmHost”class StandaloneCrmHost implements FakeCrmHostA host of the fake’s own, for a FakeCrmServer with no product’s fake around it: a package’s tests, or a demo build of a product.
- Any bearer token is a guest, and the same token the same guest: a host hands the facade the token it wants to be.
- Every one of PurrCRM’s parts is on unless
featuressays otherwise; a test may change it at any time. - A guest’s notices are kept in
notices, and each is pushed onpushesas FCM would hand it to the phone, for a demo to pass to Crm.handlePushArrival.
Members of StandaloneCrmHost
Section titled “Members of StandaloneCrmHost”StandaloneCrmHost()
Section titled “StandaloneCrmHost()”StandaloneCrmHost({DateTime Function()? clock, Iterable<CrmFeature> features = const [CrmFeature.messenger, CrmFeature.helpCenter, CrmFeature.inAppMessages]})eventsOff
Section titled “eventsOff”final Set<String> eventsOffThe guests who turned the product events off.
features
Section titled “features”final Set<CrmFeature> featuresPurrCRM’s parts that are on.
notices
Section titled “notices”final List<({String guestId, String id, FakeCrmNotice notice})> noticesEvery notice PurrCRM sent, oldest first, with the guest it was for.
pushes
Section titled “pushes”Stream<Map<String, String>> get pushesThe data of every push, as FCM would hand it to the phone: its type, its notice’s id and its link.
Future<void> close()Ends pushes.
guestFor
Section titled “guestFor”String? guestFor(String token)The token itself; a guest joins the first time their token is seen.
crmFixtureDecoders
Section titled “crmFixtureDecoders”final Map<String, Object Function(Map<String, Object?>)> crmFixtureDecodersEvery fixture a model of the package’s reads, by file name, with the model’s decoder.
fakeMessengerLayout
Section titled “fakeMessengerLayout”const Map<String, Object?> fakeMessengerLayoutA home the app’s staff published, for a demo and a test of the fake (FakeCrmServer.messengerLayout): a greeting with the guest’s name, Alex and Sam’s faces with their names, an away message, a status, the send card, a link into the product, the help center’s search and its featured articles, then the guest’s conversations and tickets.
fakeNewsTopic
Section titled “fakeNewsTopic”const String fakeNewsTopicThe fake’s News topic’s id.
fakeOffersTopic
Section titled “fakeOffersTopic”const String fakeOffersTopicThe fake’s Offers topic’s id.