diff --git a/most-typings.d.ts b/most-typings.d.ts index cd09955..fe20113 100644 --- a/most-typings.d.ts +++ b/most-typings.d.ts @@ -6,6 +6,7 @@ declare module './lib' { token: Token updates: Stream responses: Stream + selectResponses (query: Partial<{ responseType: t.Type, method: string }>): Stream events (eventName: string): Stream dispose (): void } diff --git a/rxjs-typings.d.ts b/rxjs-typings.d.ts index 40c36b1..e97ea82 100644 --- a/rxjs-typings.d.ts +++ b/rxjs-typings.d.ts @@ -1,12 +1,14 @@ import { TcombUpdate, TcombUpdatesState, Token } from './lib' +import { ComponentSinks, ComponentSources } from './lib/plugins' import { Observable } from 'rxjs' -import { ComponentSinks, ComponentSources } from './lib/plugins'; +import * as t from 'tcomb' declare module './lib' { interface DriverExecution { token: Token updates: Observable responses: Observable + selectResponses (query: Partial<{ responseType: t.Type, method: string }>): Observable events (eventName: string): Observable dispose (): void } diff --git a/src/interfaces.ts b/src/interfaces.ts index 7ea2bfb..22762f6 100644 --- a/src/interfaces.ts +++ b/src/interfaces.ts @@ -1,5 +1,6 @@ import { Observable } from 'rxjs' import { TcombWebhookResponse, TcombRequest, TcombUpdate, TcombUpdatesState } from './runtime-types/types' +import * as t from 'tcomb' export type Token = string export type GenericStream = any @@ -34,27 +35,37 @@ export interface DriverExecution { token: Token updates: GenericStream responses: GenericStream + selectResponses (query: Partial<{ responseType: t.Type, method: string }>): GenericStream events (eventName: string): GenericStream dispose (): void } -export interface TelegramAPIRequest { - token: Token - method: string - query: any - httpMethod?: string -} +export namespace TelegramAPI { + export interface Request { + token: Token + method: string + query: any + httpMethod?: string + returnType?: t.Type + } -export interface TelegramAPIError { - ok: boolean - description: string - error_code: number -} + export interface ResponseParameters { + migrate_to_chat_id: string + retry_after: number + } + + export interface Error { + ok: boolean + description: string + error_code: number + parameters?: ResponseParameters + } -export interface TelegramAPIResponseResult {} + export interface ResponseResult {} -export interface TelegramAPIResponse { - ok: boolean - description?: string - result: TelegramAPIResponseResult + export interface Response { + ok: boolean + description?: string + result: ResponseResult + } } diff --git a/src/runtime-types/types.ts b/src/runtime-types/types.ts index d142ba3..0c4f2a3 100644 --- a/src/runtime-types/types.ts +++ b/src/runtime-types/types.ts @@ -335,14 +335,15 @@ export const Request = t.struct({ type: t.enums.of(['sink']), multipart: t.maybe(t.Boolean), method: t.String, - returnType: t.maybe(t.String), + // TODO: stricter types + returnType: t.maybe((t as any).Type), options: t.Object }) export interface TcombRequest { type: 'sink', multipart?: boolean, method: string, - returnType?: string + returnType?: t.Type options: any } diff --git a/src/telegram-driver/api-request.ts b/src/telegram-driver/api-request.ts index 1a7b457..1b9d874 100644 --- a/src/telegram-driver/api-request.ts +++ b/src/telegram-driver/api-request.ts @@ -1,10 +1,15 @@ -import { TelegramAPIRequest, TelegramAPIResponse, TelegramAPIResponseResult, TelegramAPIError } from '../interfaces' +import { TelegramAPI } from '../interfaces' import { Observable, Observer, Observable as $ } from 'rxjs' import * as request from 'superagent' import { Request, Response } from 'superagent' import { propOr, last, values, pipe, mapObjIndexed, curryN, ifElse } from 'ramda' +export type OriginalResponseStream = Observable +export type ResponseStream = + Observable & + { request: TelegramAPI.Request } + let fromSuperagent = (request: Request): Observable => $.create((obs: Observer): () => void => { request.end((err, res) => { @@ -31,24 +36,37 @@ let transformReq = curryN(2, (req: Request, multipart: boolean) => ifElse( req.send.bind(req) )) -export function makeAPIRequest ( - { - token, - method, - query, - httpMethod = 'POST' - }: TelegramAPIRequest, - multipart = false -): Observable { +function createResponse ( + { token, method, query, httpMethod = 'POST' }: TelegramAPI.Request, + multipart: boolean +): OriginalResponseStream { let endpoint = `https://api.telegram.org/bot${token}` let url = `${endpoint}/${method}` let req = transformReq(request(httpMethod, url).redirects(0), multipart)(query) return fromSuperagent(req) .catch(e => $.throw(e instanceof Error ? e : new Error(e))) - .map(res => res.body) + .map(res => res.body) .map(body => body.ok ? $.of(body.result) : $.throw(body)) - .switch() +} + +function makeRequestToResponse (request: TelegramAPI.Request) { + return function requestToResponse (response: OriginalResponseStream): ResponseStream { + Object.defineProperty(response, 'request', { + value: request, + writable: false + }) + + return (response as ResponseStream) + } +} + +export function makeAPIRequest ( + apiReq: TelegramAPI.Request, + multipart = false +) { + return createResponse(apiReq, multipart) + .map(makeRequestToResponse(apiReq)) } diff --git a/src/telegram-driver/sinks.ts b/src/telegram-driver/sinks.ts index 766d0ad..b91a738 100755 --- a/src/telegram-driver/sinks.ts +++ b/src/telegram-driver/sinks.ts @@ -1,4 +1,4 @@ -import { Request, WebhookResponse } from '../runtime-types' +import { Request, Chat, WebhookResponse } from '../runtime-types' /* tslint:disable */ import { TcombRequest, TcombWebhookResponse, TcombUpdate } from '../runtime-types/types' /* tslint:enable */ @@ -397,6 +397,7 @@ export let getChat = curryN(2, ({ chat_id }: SinkPayload, update: Update) => { return Request({ type: 'sink', method: 'getChat', + returnType: Chat, options }) }) diff --git a/src/telegram-driver/sources.ts b/src/telegram-driver/sources.ts index 49c6358..77c5327 100644 --- a/src/telegram-driver/sources.ts +++ b/src/telegram-driver/sources.ts @@ -29,7 +29,7 @@ let makeUpdatesResolver = token, method: 'getUpdates', query: { offset, timeout: 60000 } - })) + }).switch()) export function makeUpdates (initialState: TcombUpdatesState, token: Token): Observable { UpdatesState(initialState) diff --git a/src/telegram-driver/telegram-driver.ts b/src/telegram-driver/telegram-driver.ts index c9a5bc4..5e9f189 100644 --- a/src/telegram-driver/telegram-driver.ts +++ b/src/telegram-driver/telegram-driver.ts @@ -1,5 +1,6 @@ import { Observable, Subject, Observable as $ } from 'rxjs' -import { mapObjIndexed } from 'ramda' +import { T, F, tryCatch, last, always, and, is, identity, mapObjIndexed, both, invoker, cond } from 'ramda' +import { isType, Type } from 'tcomb' import { DriverOptions, @@ -35,6 +36,36 @@ let makeEventsSelector = 'callback_query': callbackQuery.share() })[eventName] +function makeResponsesSelector (res: Observable) { + const emptyType = Object.assign(identity.bind({}), { is: () => true }) + const filter = (invoker as any)(1, 'filter') + const tryFilter = (x: any) => filter((tryCatch as any)(x, F)) + + return function responses ({ + responseType, + method + }: { responseType?: Type, method?: string } = {}): Observable { + const responseFilter = ([_, v]: any) => responseType.is(responseType(v)) + const methodRFilter = ([{ request: { returnType: Type = emptyType } }, v]: any) => Type.is(Type(v)) + const requestFilter = ([{ request: { returnType: Type = responseType } }, v]: any) => Type.is(Type(v)) + const methodFilter = ([{ request: r }]: any) => r.method === method + let selectedRes = cond([ + [ + always(and(isType(responseType), !method)), + tryFilter(responseFilter)], + [ + always(and(is(String, method), !responseType)), + tryFilter(both(methodFilter, methodRFilter))], + [ + always(and(isType(responseType), is(String, method))), + tryFilter(both(methodFilter, requestFilter))], + [T, identity] + ])($.zip(res, res.switch())) as Observable<[any, any]> + + return selectedRes.map(last) + } +} + let handleWebhook = ( token: Token, request: Observable>, @@ -54,8 +85,9 @@ let handleRequest = .flatMap(({ method, multipart, + returnType, options: query - }) => makeAPIRequest({token, method, query}, multipart)) + }) => makeAPIRequest({returnType, token, method, query}, multipart)) export function makeTelegramDriver ( token: Token, @@ -68,7 +100,7 @@ export function makeTelegramDriver ( } let proxyUpdates = options.skipUpdates ? $.never() : makeUpdates(state, token) - let proxyWebHook = new Subject() + let proxyWebHook = new Subject() if (options.webhook) { proxyUpdates = makeWebHook(state, proxyWebHook) @@ -100,8 +132,9 @@ export function makeTelegramDriver ( }, mapObjIndexed(adapt, { events: makeEventsSelector(sources), - updates, - responses + selectResponses: makeResponsesSelector(responses), + responses: responses.switch(), + updates })) as DriverExecution } diff --git a/test/integration/index/most.ts b/test/integration/index/most.ts index df98e3a..5594f02 100755 --- a/test/integration/index/most.ts +++ b/test/integration/index/most.ts @@ -89,30 +89,32 @@ if (isRecord) { console.log('Recording mode') } -const onError: OnErrorFn = (sources, t) => (err) => { - sources.bot.dispose() +type Dispose = Sources['bot']['dispose'] + +const onError: OnErrorFn = (dispose, t) => (err) => { + dispose() console.error('test error: ', err) t.fail(err) t.end() } -const okTake: OkTakeFn = (t, sources, next, error = onError(sources, t)) => { - sources.bot.responses +const okTake: OkTakeFn = (t, source, dispose, next, error = onError(dispose, t)) => { + source .take(1) .subscribe({ next (m: T) { - sources.bot.dispose() + dispose() next(m) }, error, complete: () => undefined }) } -const okDrop: OkDropFn = (t, sources, next, error = onError(sources, t)) => { - sources.bot.responses +const okDrop: OkDropFn = (t, source, dispose, next, error = onError(dispose, t)) => { + source .skip(1) .subscribe({ next (m: T) { - sources.bot.dispose() + dispose() next(m) }, error, @@ -128,10 +130,10 @@ test('should get me with basic driver', t => { ]) }) let { sources, run } = Cycle(main, { bot: basicDriver }) + let { bot: { selectResponses, dispose } } = sources run() - okTake(t, sources, (user) => { - t.ok(User.is(User(user)), 'user satisfies typecheck') + okTake(t, selectResponses({ responseType: User }), dispose, (user) => { t.ok(user.hasOwnProperty('id'), 'user object has property id') t.ok(user.hasOwnProperty('first_name'), 'user object has property first_name') t.ok(user.hasOwnProperty('username'), 'user object has property username') @@ -147,10 +149,10 @@ test('should get webhook info with basic driver', t => { ]) }) let { sources, run } = Cycle(main, { bot: basicDriver }) + let { bot: { selectResponses, dispose } } = sources run() - okTake(t, sources, (info) => { - t.ok(WebhookInfo.is(WebhookInfo(info)), 'webhook info satisfies typecheck') + okTake(t, selectResponses({ responseType: WebhookInfo }), dispose, (info) => { t.end() }) }) @@ -163,10 +165,10 @@ test('should reply to channel posts with basic driver', t => { ]) }) let { sources, run } = Cycle(main, { bot: basicDriver }) + let { bot: { selectResponses, dispose } } = sources run() - okTake(t, sources, (message) => { - t.ok(Message.is(Message(message)), 'message satisfies typecheck') + okTake(t, selectResponses({ responseType: Message }), dispose, (message) => { t.equal(message.text, 'Cycle.js', 'message text should be equal to `Cycle.js`') t.end() }) @@ -180,10 +182,10 @@ test('should reply to edited channel posts with basic driver', t => { ]) }) let { sources, run } = Cycle(main, { bot: basicDriver }) + let { bot: { selectResponses, dispose } } = sources run() - okTake(t, sources, (message) => { - t.ok(Message.is(Message(message)), 'message satisfies typecheck') + okTake(t, selectResponses({ responseType: Message }), dispose, (message) => { t.equal(message.text, 'Cycle.js', 'message text should be equal to `Cycle.js`') t.end() }) @@ -197,10 +199,10 @@ test('should reply to edited messages with basic driver', t => { ]) }) let { sources, run } = Cycle(main, { bot: basicDriver }) + let { bot: { selectResponses, dispose } } = sources run() - okTake(t, sources, (message) => { - t.ok(Message.is(Message(message)), 'message satisfies typecheck') + okTake(t, selectResponses({ responseType: Message }), dispose, (message) => { t.equal(message.text, 'Cycle.js', 'message text should be equal to `Cycle.js`') t.end() }) @@ -214,10 +216,10 @@ test('should reply to messages with basic driver', t => { ]) }) let { sources, run } = Cycle(main, { bot: basicDriver }) + let { bot: { selectResponses, dispose } } = sources run() - okTake(t, sources, (message) => { - t.ok(Message.is(Message(message)), 'message satisfies typecheck') + okTake(t, selectResponses({ responseType: Message }), dispose, (message) => { t.equal(message.text, 'Cycle.js', 'message text should be equal to `Cycle.js`') t.end() }) @@ -233,10 +235,10 @@ test('should forward message with basic driver', t => { ]) }) let { sources, run } = Cycle(main, { bot: basicDriver }) + let { bot: { selectResponses, dispose } } = sources run() - okTake(t, sources, (message) => { - t.ok(Message.is(Message(message)), 'message satisfies typecheck') + okTake(t, selectResponses({ responseType: Message }), dispose, (message) => { t.ok(is(Object, message), 'message is object') t.end() }) @@ -252,10 +254,10 @@ test('should send photo with basic driver', t => { ]) }) let { sources, run } = Cycle(main, { bot: basicDriver }) + let { bot: { selectResponses, dispose } } = sources run() - okTake(t, sources, (message) => { - t.ok(Message.is(Message(message)), 'message satisfies typecheck') + okTake(t, selectResponses({ responseType: Message }), dispose, (message) => { t.ok(message.hasOwnProperty('photo'), 'message has property photo') t.end() }) @@ -276,10 +278,10 @@ test('should send audio with basic driver', t => { ]) }) let { sources, run } = Cycle(main, { bot: basicDriver }) + let { bot: { selectResponses, dispose } } = sources run() - okTake(t, sources, (message) => { - t.ok(Message.is(Message(message)), 'message satisfies typecheck') + okTake(t, selectResponses({ responseType: Message }), dispose, (message) => { t.equal(message.voice.mime_type, 'audio/ogg', 'mime type should be audio/ogg') t.end() }) @@ -295,10 +297,10 @@ test.skip('should send document with basic driver', t => { ]) }) let { sources, run } = Cycle(main, { bot: basicDriver }) + let { bot: { selectResponses, dispose } } = sources run() - okTake(t, sources, (message) => { - t.ok(Message.is(Message(message)), 'message satisfies typecheck') + okTake(t, selectResponses({ responseType: Message }), dispose, (message) => { t.equal(message.document.file_name, 'test.jpg', 'file name should be test.jpg') t.end() }) @@ -314,10 +316,10 @@ test.skip('should send sticker with basic driver', t => { ]) }) let { sources, run } = Cycle(main, { bot: basicDriver }) + let { bot: { selectResponses, dispose } } = sources run() - okTake(t, sources, (message) => { - t.ok(Message.is(Message(message)), 'message satisfies typecheck') + okTake(t, selectResponses({ responseType: Message }), dispose, (message) => { console.log(message) t.end() }) @@ -333,10 +335,10 @@ test.skip('should send video with basic driver', t => { ]) }) let { sources, run } = Cycle(main, { bot: basicDriver }) + let { bot: { selectResponses, dispose } } = sources run() - okTake(t, sources, (message) => { - t.ok(Message.is(Message(message)), 'message satisfies typecheck') + okTake(t, selectResponses({ responseType: Message }), dispose, (message) => { console.log(message) t.end() }) @@ -352,10 +354,10 @@ test.skip('should send voice with basic driver', t => { ]) }) let { sources, run } = Cycle(main, { bot: basicDriver }) + let { bot: { selectResponses, dispose } } = sources run() - okTake(t, sources, (message) => { - t.ok(Message.is(Message(message)), 'message satisfies typecheck') + okTake(t, selectResponses({ responseType: Message }), dispose, (message) => { console.log(message) t.end() }) @@ -371,10 +373,10 @@ test('should send location with basic driver', t => { ]) }) let { sources, run } = Cycle(main, { bot: basicDriver }) + let { bot: { selectResponses, dispose } } = sources run() - okTake(t, sources, (message) => { - t.ok(Message.is(Message(message)), 'message satisfies typecheck') + okTake(t, selectResponses({ responseType: Message }), dispose, (message) => { t.ok(message.hasOwnProperty('location'), 'message has property location') t.end() }) @@ -396,10 +398,10 @@ test('should send venue with basic driver', t => { ]) }) let { sources, run } = Cycle(main, { bot: basicDriver }) + let { bot: { selectResponses, dispose } } = sources run() - okTake(t, sources, (message) => { - t.ok(Message.is(Message(message)), 'message satisfies typecheck') + okTake(t, selectResponses({ responseType: Message }), dispose, (message) => { t.ok(message.hasOwnProperty('venue'), 'message has property venue') t.equal(message.venue.title, 'Red Square', 'venue title should be Red Square') t.equal(message.venue.address, 'Moscow, Russia', 'venue title should be Moscow, Russia') @@ -417,10 +419,10 @@ test('should send contact with basic driver', t => { ]) }) let { sources, run } = Cycle(main, { bot: basicDriver }) + let { bot: { selectResponses, dispose } } = sources run() - okTake(t, sources, (message) => { - t.ok(Message.is(Message(message)), 'message satisfies typecheck') + okTake(t, selectResponses({ responseType: Message }), dispose, (message) => { t.ok(message.hasOwnProperty('contact'), 'message has property contact') t.equal(message.contact.phone_number, '+42470', 'contact phone number should be +42470') t.equal(message.contact.first_name, 'Telegram', 'contact first name should be Telegram') @@ -438,9 +440,10 @@ test('should send chat action with basic driver', t => { ]) }) let { sources, run } = Cycle(main, { bot: basicDriver }) + let { bot: { responses, dispose } } = sources run() - okTake(t, sources, (bool) => { + okTake(t, responses, dispose, (bool) => { t.equal(bool, true, 'bool should be true') t.end() }) @@ -456,10 +459,10 @@ test('should get user profile photos with basic driver', t => { ]) }) let { sources, run } = Cycle(main, { bot: basicDriver }) + let { bot: { selectResponses, dispose } } = sources run() - okTake(t, sources, (userProfilePhotos) => { - t.ok(UserProfilePhotos.is(UserProfilePhotos(userProfilePhotos)), 'user profile photos satisfies typecheck') + okTake(t, selectResponses({ responseType: UserProfilePhotos }), dispose, (userProfilePhotos) => { t.ok(userProfilePhotos.hasOwnProperty('total_count'), 'user profile photos has property total_count') t.equal(typeof userProfilePhotos.total_count, 'number', 'total_count should be number') t.ok(userProfilePhotos.hasOwnProperty('photos'), 'user profile photos has property photos') @@ -478,10 +481,10 @@ test('should get file with basic driver', t => { ]) }) let { sources, run } = Cycle(main, { bot: basicDriver }) + let { bot: { selectResponses, dispose } } = sources run() - okTake(t, sources, (file) => { - t.ok(File.is(File(file)), 'file satisfies typecheck') + okTake(t, selectResponses({ responseType: File }), dispose, (file) => { t.equal(file.file_id, FILE_ID, 'file ids should be equal') t.end() }) @@ -497,9 +500,10 @@ test('should kick chat member with basic driver', t => { ]) }) let { sources, run } = Cycle(main, { bot: basicDriver }) + let { bot: { responses, dispose } } = sources run() - okTake(t, sources, (bool) => { + okTake(t, responses, dispose, (bool) => { t.equal(bool, true, 'bool should be true') t.end() }) @@ -515,9 +519,10 @@ test('should unban chat member with basic driver', t => { ]) }) let { sources, run } = Cycle(main, { bot: basicDriver }) + let { bot: { responses, dispose } } = sources run() - okTake(t, sources, (bool) => { + okTake(t, responses, dispose, (bool) => { t.equal(bool, true, 'bool should be true') t.end() }) @@ -531,10 +536,10 @@ test('should get chat with basic driver', t => { ]) }) let { sources, run } = Cycle(main, { bot: basicDriver }) + let { bot: { selectResponses, dispose } } = sources run() - okTake(t, sources, (chat) => { - t.ok(Chat.is(Chat(chat)), 'chat satisfies typecheck') + okTake(t, selectResponses({ responseType: Chat }), dispose, (chat) => { t.equal(chat.id, GROUP_ID) t.end() }) @@ -548,10 +553,10 @@ test('should get chat administrators with basic driver', t => { ]) }) let { sources, run } = Cycle(main, { bot: basicDriver }) + let { bot: { selectResponses, dispose } } = sources run() - okTake(t, sources, (chatMembers) => { - t.ok(tc.list(ChatMember).is(tc.list(ChatMember)(chatMembers)), 'chat members satisfies typecheck') + okTake(t, selectResponses({ responseType: tc.list(ChatMember) }), dispose, (chatMembers) => { chatMembers.forEach((chatMember: any) => { t.ok(chatMember.hasOwnProperty('user'), 'chat member has property user') t.ok(chatMember.hasOwnProperty('status'), 'chat member has property status') @@ -568,9 +573,10 @@ test('should get chat members count with basic driver', t => { ]) }) let { sources, run } = Cycle(main, { bot: basicDriver }) + let { bot: { responses, dispose } } = sources run() - okTake(t, sources, (chatMembersCount) => { + okTake(t, responses, dispose, (chatMembersCount) => { t.equal(typeof chatMembersCount, 'number') t.end() }) @@ -584,10 +590,10 @@ test('should get chat member with basic driver', t => { ]) }) let { sources, run } = Cycle(main, { bot: basicDriver }) + let { bot: { selectResponses, dispose } } = sources run() - okTake(t, sources, (chatMember) => { - t.ok(ChatMember.is(ChatMember(chatMember)), 'chat member satisfies typecheck') + okTake(t, selectResponses({ responseType: ChatMember }), dispose, (chatMember) => { t.ok(chatMember.hasOwnProperty('user'), 'chat member has property user') t.ok(chatMember.hasOwnProperty('status'), 'chat member has property status') t.equal(chatMember.user.id, 39759851, 'chat member id equals 39759851') @@ -619,9 +625,10 @@ test.skip('should answer callback query with basic driver', t => { ]) }) let { sources, run } = Cycle(main, { bot: basicDriver }) + let { bot: { responses, dispose } } = sources run() - okTake(t, sources, console.log.bind(console)) + okTake(t, responses, dispose, console.log.bind(console)) }) // Inline mode @@ -641,9 +648,10 @@ test('should edit message text with basic driver', t => { ]) }) let { sources, run } = Cycle(main, { bot: basicDriver }) + let { bot: { responses, dispose } } = sources run() - okDrop(t, sources, (message) => { + okDrop(t, responses, dispose, (message) => { if (typeof message !== 'boolean') { t.ok(Message.is(Message(message)), 'message satisfies typecheck') t.equal(message.text, 'Cycle.js') @@ -670,9 +678,10 @@ test('should edit message caption with basic driver', t => { ]) }) let { sources, run } = Cycle(main, { bot: basicDriver }) + let { bot: { responses, dispose } } = sources run() - okDrop(t, sources, (message) => { + okDrop(t, responses, dispose,(message) => { if (typeof message !== 'boolean') { t.ok(Message.is(Message(message)), 'message satisfies typecheck') t.equal(message.caption, 'Cycle.js') @@ -714,9 +723,10 @@ test('should edit message reply markup with basic driver', t => { ]) }) let { sources, run } = Cycle(main, { bot: basicDriver }) + let { bot: { responses, dispose } } = sources run() - okDrop(t, sources, (message) => { + okDrop(t, responses, dispose, (message) => { if (typeof message !== 'boolean') { t.ok(Message.is(Message(message)), 'message satisfies typecheck') t.equal(message.text, 'Message with reply_markup') @@ -746,9 +756,10 @@ test('should reply to inline query with basic driver', t => { ]) }) let { sources, run } = Cycle(main, { bot: basicDriver }) + let { bot: { responses, dispose } } = sources run() - okTake(t, sources, (bool) => { + okTake(t, responses, dispose, (bool) => { t.ok(bool, 'response should be truthy') t.end() }) @@ -762,9 +773,10 @@ test('should leave chat with basic driver', t => { ]) }) let { sources, run } = Cycle(main, { bot: basicDriver }) + let { bot: { responses, dispose } } = sources run() - okTake(t, sources, (bool) => { + okTake(t, responses, dispose, (bool) => { t.equal(bool, true, 'bool should be true') t.end() }) @@ -783,10 +795,10 @@ test('should send game with basic driver', t => { ]) }) let { sources, run } = Cycle(main, { bot: basicDriver }) + let { bot: { selectResponses, dispose } } = sources run() - okTake(t, sources, (message) => { - t.ok(Message.is(Message(message)), 'message satisfies typecheck') + okTake(t, selectResponses({ responseType: Message }), dispose, (message) => { t.end() }) }) @@ -878,10 +890,10 @@ test('should get game high scores with basic driver', t => { ]) }) let { sources, run } = Cycle(main, { bot: basicDriver }) + let { bot: { selectResponses, dispose } } = sources run() - okDrop(t, sources, (gameHighScores) => { - t.ok(tc.list(GameHighScore).is(tc.list(GameHighScore)(gameHighScores)), 'game high scores satisfies typecheck') + okTake(t, selectResponses({ responseType: tc.list(GameHighScore) }), dispose, (gameHighScores) => { t.end() }) }) diff --git a/test/integration/index/rxjs.ts b/test/integration/index/rxjs.ts index 7a24a07..1ecb125 100755 --- a/test/integration/index/rxjs.ts +++ b/test/integration/index/rxjs.ts @@ -89,30 +89,32 @@ if (isRecord) { console.log('Recording mode') } -const onError: OnErrorFn = (sources, t) => (err) => { - sources.bot.dispose() +type Dispose = Sources['bot']['dispose'] + +const onError: OnErrorFn = (dispose, t) => (err) => { + dispose() console.error('test error: ', err) t.fail(err) t.end() } -const okTake: OkTakeFn = (t, sources, next, error = onError(sources, t)) => { - sources.bot.responses +const okTake: OkTakeFn = (t, source, dispose, next, error = onError(dispose, t)) => { + source .take(1) .subscribe( (m: T) => { - sources.bot.dispose() + dispose() next(m) }, error, () => undefined ) } -const okDrop: OkDropFn = (t, sources, next, error = onError(sources, t)) => { - sources.bot.responses +const okDrop: OkDropFn = (t, source, dispose, next, error = onError(dispose, t)) => { + source .skip(1) .subscribe( (m: T) => { - sources.bot.dispose() + dispose() next(m) }, error, @@ -128,10 +130,10 @@ test('should get me with basic driver', t => { ]) }) let { sources, run } = Cycle(main, { bot: basicDriver }) + let { bot: { selectResponses, dispose } } = sources run() - okTake(t, sources, (user) => { - t.ok(User.is(User(user)), 'user satisfies typecheck') + okTake(t, selectResponses({ responseType: User }), dispose, (user) => { t.ok(user.hasOwnProperty('id'), 'user object has property id') t.ok(user.hasOwnProperty('first_name'), 'user object has property first_name') t.ok(user.hasOwnProperty('username'), 'user object has property username') @@ -147,10 +149,10 @@ test('should get webhook info with basic driver', t => { ]) }) let { sources, run } = Cycle(main, { bot: basicDriver }) + let { bot: { selectResponses, dispose } } = sources run() - okTake(t, sources, (info) => { - t.ok(WebhookInfo.is(WebhookInfo(info)), 'webhook info satisfies typecheck') + okTake(t, selectResponses({ responseType: WebhookInfo }), dispose, (info) => { t.end() }) }) @@ -163,10 +165,10 @@ test('should reply to channel posts with basic driver', t => { ]) }) let { sources, run } = Cycle(main, { bot: basicDriver }) + let { bot: { selectResponses, dispose } } = sources run() - okTake(t, sources, (message) => { - t.ok(Message.is(Message(message)), 'message satisfies typecheck') + okTake(t, selectResponses({ responseType: Message }), dispose, (message) => { t.equal(message.text, 'Cycle.js', 'message text should be equal to `Cycle.js`') t.end() }) @@ -180,10 +182,10 @@ test('should reply to edited channel posts with basic driver', t => { ]) }) let { sources, run } = Cycle(main, { bot: basicDriver }) + let { bot: { selectResponses, dispose } } = sources run() - okTake(t, sources, (message) => { - t.ok(Message.is(Message(message)), 'message satisfies typecheck') + okTake(t, selectResponses({ responseType: Message }), dispose, (message) => { t.equal(message.text, 'Cycle.js', 'message text should be equal to `Cycle.js`') t.end() }) @@ -197,10 +199,10 @@ test('should reply to edited messages with basic driver', t => { ]) }) let { sources, run } = Cycle(main, { bot: basicDriver }) + let { bot: { selectResponses, dispose } } = sources run() - okTake(t, sources, (message) => { - t.ok(Message.is(Message(message)), 'message satisfies typecheck') + okTake(t, selectResponses({ responseType: Message }), dispose, (message) => { t.equal(message.text, 'Cycle.js', 'message text should be equal to `Cycle.js`') t.end() }) @@ -214,10 +216,10 @@ test('should reply to messages with basic driver', t => { ]) }) let { sources, run } = Cycle(main, { bot: basicDriver }) + let { bot: { selectResponses, dispose } } = sources run() - okTake(t, sources, (message) => { - t.ok(Message.is(Message(message)), 'message satisfies typecheck') + okTake(t, selectResponses({ responseType: Message }), dispose, (message) => { t.equal(message.text, 'Cycle.js', 'message text should be equal to `Cycle.js`') t.end() }) @@ -233,10 +235,10 @@ test('should forward message with basic driver', t => { ]) }) let { sources, run } = Cycle(main, { bot: basicDriver }) + let { bot: { selectResponses, dispose } } = sources run() - okTake(t, sources, (message) => { - t.ok(Message.is(Message(message)), 'message satisfies typecheck') + okTake(t, selectResponses({ responseType: Message }), dispose, (message) => { t.ok(is(Object, message), 'message is object') t.end() }) @@ -252,10 +254,10 @@ test('should send photo with basic driver', t => { ]) }) let { sources, run } = Cycle(main, { bot: basicDriver }) + let { bot: { selectResponses, dispose } } = sources run() - okTake(t, sources, (message) => { - t.ok(Message.is(Message(message)), 'message satisfies typecheck') + okTake(t, selectResponses({ responseType: Message }), dispose, (message) => { t.ok(message.hasOwnProperty('photo'), 'message has property photo') t.end() }) @@ -276,10 +278,10 @@ test('should send audio with basic driver', t => { ]) }) let { sources, run } = Cycle(main, { bot: basicDriver }) + let { bot: { selectResponses, dispose } } = sources run() - okTake(t, sources, (message) => { - t.ok(Message.is(Message(message)), 'message satisfies typecheck') + okTake(t, selectResponses({ responseType: Message }), dispose, (message) => { t.equal(message.voice.mime_type, 'audio/ogg', 'mime type should be audio/ogg') t.end() }) @@ -295,10 +297,10 @@ test.skip('should send document with basic driver', t => { ]) }) let { sources, run } = Cycle(main, { bot: basicDriver }) + let { bot: { selectResponses, dispose } } = sources run() - okTake(t, sources, (message) => { - t.ok(Message.is(Message(message)), 'message satisfies typecheck') + okTake(t, selectResponses({ responseType: Message }), dispose, (message) => { t.equal(message.document.file_name, 'test.jpg', 'file name should be test.jpg') t.end() }) @@ -314,10 +316,10 @@ test.skip('should send sticker with basic driver', t => { ]) }) let { sources, run } = Cycle(main, { bot: basicDriver }) + let { bot: { selectResponses, dispose } } = sources run() - okTake(t, sources, (message) => { - t.ok(Message.is(Message(message)), 'message satisfies typecheck') + okTake(t, selectResponses({ responseType: Message }), dispose, (message) => { console.log(message) t.end() }) @@ -333,10 +335,10 @@ test.skip('should send video with basic driver', t => { ]) }) let { sources, run } = Cycle(main, { bot: basicDriver }) + let { bot: { selectResponses, dispose } } = sources run() - okTake(t, sources, (message) => { - t.ok(Message.is(Message(message)), 'message satisfies typecheck') + okTake(t, selectResponses({ responseType: Message }), dispose, (message) => { console.log(message) t.end() }) @@ -352,10 +354,10 @@ test.skip('should send voice with basic driver', t => { ]) }) let { sources, run } = Cycle(main, { bot: basicDriver }) + let { bot: { selectResponses, dispose } } = sources run() - okTake(t, sources, (message) => { - t.ok(Message.is(Message(message)), 'message satisfies typecheck') + okTake(t, selectResponses({ responseType: Message }), dispose, (message) => { console.log(message) t.end() }) @@ -371,10 +373,10 @@ test('should send location with basic driver', t => { ]) }) let { sources, run } = Cycle(main, { bot: basicDriver }) + let { bot: { selectResponses, dispose } } = sources run() - okTake(t, sources, (message) => { - t.ok(Message.is(Message(message)), 'message satisfies typecheck') + okTake(t, selectResponses({ responseType: Message }), dispose, (message) => { t.ok(message.hasOwnProperty('location'), 'message has property location') t.end() }) @@ -396,10 +398,10 @@ test('should send venue with basic driver', t => { ]) }) let { sources, run } = Cycle(main, { bot: basicDriver }) + let { bot: { selectResponses, dispose } } = sources run() - okTake(t, sources, (message) => { - t.ok(Message.is(Message(message)), 'message satisfies typecheck') + okTake(t, selectResponses({ responseType: Message }), dispose, (message) => { t.ok(message.hasOwnProperty('venue'), 'message has property venue') t.equal(message.venue.title, 'Red Square', 'venue title should be Red Square') t.equal(message.venue.address, 'Moscow, Russia', 'venue title should be Moscow, Russia') @@ -417,10 +419,10 @@ test('should send contact with basic driver', t => { ]) }) let { sources, run } = Cycle(main, { bot: basicDriver }) + let { bot: { selectResponses, dispose } } = sources run() - okTake(t, sources, (message) => { - t.ok(Message.is(Message(message)), 'message satisfies typecheck') + okTake(t, selectResponses({ responseType: Message }), dispose, (message) => { t.ok(message.hasOwnProperty('contact'), 'message has property contact') t.equal(message.contact.phone_number, '+42470', 'contact phone number should be +42470') t.equal(message.contact.first_name, 'Telegram', 'contact first name should be Telegram') @@ -438,10 +440,11 @@ test('should send chat action with basic driver', t => { ]) }) let { sources, run } = Cycle(main, { bot: basicDriver }) + let { bot: { responses, dispose } } = sources run() - okTake(t, sources, (bool) => { - t.equal(bool, true, 'bool should be true') + okTake(t, responses, dispose, (bool) => { + t.ok(bool, 'response should be truthy') t.end() }) }) @@ -456,10 +459,10 @@ test('should get user profile photos with basic driver', t => { ]) }) let { sources, run } = Cycle(main, { bot: basicDriver }) + let { bot: { selectResponses, dispose } } = sources run() - okTake(t, sources, (userProfilePhotos) => { - t.ok(UserProfilePhotos.is(UserProfilePhotos(userProfilePhotos)), 'user profile photos satisfies typecheck') + okTake(t, selectResponses({ responseType: UserProfilePhotos }), dispose, (userProfilePhotos) => { t.ok(userProfilePhotos.hasOwnProperty('total_count'), 'user profile photos has property total_count') t.equal(typeof userProfilePhotos.total_count, 'number', 'total_count should be number') t.ok(userProfilePhotos.hasOwnProperty('photos'), 'user profile photos has property photos') @@ -478,10 +481,10 @@ test('should get file with basic driver', t => { ]) }) let { sources, run } = Cycle(main, { bot: basicDriver }) + let { bot: { selectResponses, dispose } } = sources run() - okTake(t, sources, (file) => { - t.ok(File.is(File(file)), 'file satisfies typecheck') + okTake(t, selectResponses({ responseType: File }), dispose, (file) => { t.equal(file.file_id, FILE_ID, 'file ids should be equal') t.end() }) @@ -497,10 +500,11 @@ test('should kick chat member with basic driver', t => { ]) }) let { sources, run } = Cycle(main, { bot: basicDriver }) + let { bot: { responses, dispose } } = sources run() - okTake(t, sources, (bool) => { - t.equal(bool, true, 'bool should be true') + okTake(t, responses, dispose, (bool) => { + t.ok(bool, 'response should be truthy') t.end() }) }) @@ -515,10 +519,11 @@ test('should unban chat member with basic driver', t => { ]) }) let { sources, run } = Cycle(main, { bot: basicDriver }) + let { bot: { responses, dispose } } = sources run() - okTake(t, sources, (bool) => { - t.equal(bool, true, 'bool should be true') + okTake(t, responses, dispose, (bool) => { + t.ok(bool, 'response should be truthy') t.end() }) }) @@ -531,10 +536,10 @@ test('should get chat with basic driver', t => { ]) }) let { sources, run } = Cycle(main, { bot: basicDriver }) + let { bot: { selectResponses, dispose } } = sources run() - okTake(t, sources, (chat) => { - t.ok(Chat.is(Chat(chat)), 'chat satisfies typecheck') + okTake(t, selectResponses({ responseType: Chat }), dispose, (chat) => { t.equal(chat.id, GROUP_ID) t.end() }) @@ -548,10 +553,10 @@ test('should get chat administrators with basic driver', t => { ]) }) let { sources, run } = Cycle(main, { bot: basicDriver }) + let { bot: { selectResponses, dispose } } = sources run() - okTake(t, sources, (chatMembers) => { - t.ok(tc.list(ChatMember).is(tc.list(ChatMember)(chatMembers)), 'chat members satisfies typecheck') + okTake(t, selectResponses({ responseType: tc.list(ChatMember) }), dispose, (chatMembers) => { chatMembers.forEach((chatMember: any) => { t.ok(chatMember.hasOwnProperty('user'), 'chat member has property user') t.ok(chatMember.hasOwnProperty('status'), 'chat member has property status') @@ -568,9 +573,10 @@ test('should get chat members count with basic driver', t => { ]) }) let { sources, run } = Cycle(main, { bot: basicDriver }) + let { bot: { responses, dispose } } = sources run() - okTake(t, sources, (chatMembersCount) => { + okTake(t, responses, dispose, (chatMembersCount) => { t.equal(typeof chatMembersCount, 'number') t.end() }) @@ -584,10 +590,10 @@ test('should get chat member with basic driver', t => { ]) }) let { sources, run } = Cycle(main, { bot: basicDriver }) + let { bot: { selectResponses, dispose } } = sources run() - okTake(t, sources, (chatMember) => { - t.ok(ChatMember.is(ChatMember(chatMember)), 'chat member satisfies typecheck') + okTake(t, selectResponses({ responseType: ChatMember }), dispose, (chatMember) => { t.ok(chatMember.hasOwnProperty('user'), 'chat member has property user') t.ok(chatMember.hasOwnProperty('status'), 'chat member has property status') t.equal(chatMember.user.id, 39759851, 'chat member id equals 39759851') @@ -619,13 +625,14 @@ test.skip('should answer callback query with basic driver', t => { ]) }) let { sources, run } = Cycle(main, { bot: basicDriver }) + let { bot: { selectResponses, dispose } } = sources run() sources.bot.responses .take(1) .subscribe( console.log.bind(console), - onError(sources, t)) + onError(dispose, t)) }) // Inline mode @@ -644,9 +651,10 @@ test('should edit message text with basic driver', t => { ]) }) let { sources, run } = Cycle(main, { bot: basicDriver }) + let { bot: { responses, dispose } } = sources run() - okDrop(t, sources, (message) => { + okDrop(t, responses, dispose, (message) => { if (typeof message !== 'boolean') { t.ok(Message.is(Message(message)), 'message satisfies typecheck') t.equal(message.text, 'Cycle.js') @@ -672,9 +680,10 @@ test('should edit message caption with basic driver', t => { ]) }) let { sources, run } = Cycle(main, { bot: basicDriver }) + let { bot: { responses, dispose } } = sources run() - okDrop(t, sources, (message) => { + okDrop(t, responses, dispose, (message) => { if (typeof message !== 'boolean') { t.ok(Message.is(Message(message)), 'message satisfies typecheck') t.equal(message.caption, 'Cycle.js') @@ -715,9 +724,10 @@ test('should edit message reply markup with basic driver', t => { ]) }) let { sources, run } = Cycle(main, { bot: basicDriver }) + let { bot: { responses, dispose } } = sources run() - okDrop(t, sources, (message) => { + okDrop(t, responses, dispose, (message) => { if (typeof message !== 'boolean') { t.ok(Message.is(Message(message)), 'message satisfies typecheck') t.equal(message.text, 'Message with reply_markup') @@ -746,9 +756,10 @@ test('should reply to inline query with basic driver', t => { ]) }) let { sources, run } = Cycle(main, { bot: basicDriver }) + let { bot: { responses, dispose } } = sources run() - okTake(t, sources, (bool) => { + okTake(t, responses, dispose, (bool) => { t.ok(bool, 'response should be truthy') t.end() }) @@ -762,10 +773,11 @@ test('should leave chat with basic driver', t => { ]) }) let { sources, run } = Cycle(main, { bot: basicDriver }) + let { bot: { responses, dispose } } = sources run() - okTake(t, sources, (bool) => { - t.equal(bool, true, 'bool should be true') + okTake(t, responses, dispose, (bool) => { + t.ok(bool, 'response should be truthy') t.end() }) }) @@ -783,10 +795,10 @@ test('should send game with basic driver', t => { ]) }) let { sources, run } = Cycle(main, { bot: basicDriver }) + let { bot: { selectResponses, dispose } } = sources run() - okTake(t, sources, (message) => { - t.ok(Message.is(Message(message)), 'message satisfies typecheck') + okTake(t, selectResponses({ responseType: Message }), dispose, (message) => { t.end() }) }) @@ -885,10 +897,10 @@ test('should get game high scores with basic driver', t => { ]) }) let { sources, run } = Cycle(main, { bot: basicDriver }) + let { bot: { selectResponses, dispose } } = sources run() - okDrop(t, sources, (gameHighScores) => { - t.ok(tc.list(GameHighScore).is(tc.list(GameHighScore)(gameHighScores)), 'game high scores satisfies typecheck') + okTake(t, selectResponses({ responseType: tc.list(GameHighScore) }), dispose, (gameHighScores) => { t.end() }) }) diff --git a/test/integration/index/xstream.ts b/test/integration/index/xstream.ts index 2fd2dc4..2148779 100644 --- a/test/integration/index/xstream.ts +++ b/test/integration/index/xstream.ts @@ -89,30 +89,32 @@ if (isRecord) { console.log('Recording mode') } -const onError: OnErrorFn = (sources, t) => (err) => { - sources.bot.dispose() +type Dispose = Sources['bot']['dispose'] + +const onError: OnErrorFn = (dispose, t) => (err) => { + dispose() console.error('test error: ', err) t.fail(err) t.end() } -const okTake: OkTakeFn = (t, sources, next, error = onError(sources, t)) => { - sources.bot.responses +const okTake: OkTakeFn = (t, source, dispose, next, error = onError(dispose, t)) => { + source .take(1) .addListener({ next (m: T) { - sources.bot.dispose() + dispose() next(m) }, error, complete: () => undefined }) } -const okDrop: OkDropFn = (t, sources, next, error = onError(sources, t)) => { - sources.bot.responses +const okDrop: OkDropFn = (t, source, dispose, next, error = onError(dispose, t)) => { + source .drop(1) .addListener({ next (m: T) { - sources.bot.dispose() + dispose() next(m) }, error, @@ -128,10 +130,10 @@ test('should get me with basic driver', t => { ]) }) let { sources, run } = Cycle(main, { bot: basicDriver }) + let { bot: { selectResponses, dispose } } = sources run() - okTake(t, sources, (user) => { - t.ok(User.is(User(user)), 'user satisfies typecheck') + okTake(t, selectResponses({ responseType: User }), dispose, (user) => { t.ok(user.hasOwnProperty('id'), 'user object has property id') t.ok(user.hasOwnProperty('first_name'), 'user object has property first_name') t.ok(user.hasOwnProperty('username'), 'user object has property username') @@ -147,9 +149,10 @@ test('should get webhook info with basic driver', t => { ]) }) let { sources, run } = Cycle(main, { bot: basicDriver }) + let { bot: { selectResponses, dispose } } = sources run() - okTake(t, sources, (info) => { + okTake(t, selectResponses({ responseType: WebhookInfo }), dispose, (info) => { t.ok(WebhookInfo.is(WebhookInfo(info)), 'webhook info satisfies typecheck') t.end() }) @@ -163,9 +166,10 @@ test('should reply to channel posts with basic driver', t => { ]) }) let { sources, run } = Cycle(main, { bot: basicDriver }) + let { bot: { selectResponses, dispose } } = sources run() - okTake(t, sources, (message) => { + okTake(t, selectResponses({ responseType: Message }), dispose, (message) => { t.ok(Message.is(Message(message)), 'message satisfies typecheck') t.equal(message.text, 'Cycle.js', 'message text should be equal to `Cycle.js`') t.end() @@ -180,10 +184,10 @@ test('should reply to edited channel posts with basic driver', t => { ]) }) let { sources, run } = Cycle(main, { bot: basicDriver }) + let { bot: { selectResponses, dispose } } = sources run() - okTake(t, sources, (message) => { - t.ok(Message.is(Message(message)), 'message satisfies typecheck') + okTake(t, selectResponses({ responseType: Message }), dispose, (message) => { t.equal(message.text, 'Cycle.js', 'message text should be equal to `Cycle.js`') t.end() }) @@ -197,10 +201,10 @@ test('should reply to edited messages with basic driver', t => { ]) }) let { sources, run } = Cycle(main, { bot: basicDriver }) + let { bot: { selectResponses, dispose } } = sources run() - okTake(t, sources, (message) => { - t.ok(Message.is(Message(message)), 'message satisfies typecheck') + okTake(t, selectResponses({ responseType: Message }), dispose, (message) => { t.equal(message.text, 'Cycle.js', 'message text should be equal to `Cycle.js`') t.end() }) @@ -214,10 +218,10 @@ test('should reply to messages with basic driver', t => { ]) }) let { sources, run } = Cycle(main, { bot: basicDriver }) + let { bot: { selectResponses, dispose } } = sources run() - okTake(t, sources, (message) => { - t.ok(Message.is(Message(message)), 'message satisfies typecheck') + okTake(t, selectResponses({ responseType: Message }), dispose, (message) => { t.equal(message.text, 'Cycle.js', 'message text should be equal to `Cycle.js`') t.end() }) @@ -233,10 +237,10 @@ test('should forward message with basic driver', t => { ]) }) let { sources, run } = Cycle(main, { bot: basicDriver }) + let { bot: { selectResponses, dispose } } = sources run() - okTake(t, sources, (message) => { - t.ok(Message.is(Message(message)), 'message satisfies typecheck') + okTake(t, selectResponses({ responseType: Message }), dispose, (message) => { t.ok(is(Object, message), 'message is object') t.end() }) @@ -252,10 +256,10 @@ test('should send photo with basic driver', t => { ]) }) let { sources, run } = Cycle(main, { bot: basicDriver }) + let { bot: { selectResponses, dispose } } = sources run() - okTake(t, sources, (message) => { - t.ok(Message.is(Message(message)), 'message satisfies typecheck') + okTake(t, selectResponses({ responseType: Message }), dispose, (message) => { t.ok(message.hasOwnProperty('photo'), 'message has property photo') t.end() }) @@ -276,10 +280,10 @@ test('should send audio with basic driver', t => { ]) }) let { sources, run } = Cycle(main, { bot: basicDriver }) + let { bot: { selectResponses, dispose } } = sources run() - okTake(t, sources, (message) => { - t.ok(Message.is(Message(message)), 'message satisfies typecheck') + okTake(t, selectResponses({ responseType: Message }), dispose, (message) => { t.equal(message.voice.mime_type, 'audio/ogg', 'mime type should be audio/ogg') t.end() }) @@ -295,10 +299,10 @@ test.skip('should send document with basic driver', t => { ]) }) let { sources, run } = Cycle(main, { bot: basicDriver }) + let { bot: { selectResponses, dispose } } = sources run() - okTake(t, sources, (message) => { - t.ok(Message.is(Message(message)), 'message satisfies typecheck') + okTake(t, selectResponses({ responseType: Message }), dispose, (message) => { t.equal(message.document.file_name, 'test.jpg', 'file name should be test.jpg') t.end() }) @@ -314,10 +318,10 @@ test.skip('should send sticker with basic driver', t => { ]) }) let { sources, run } = Cycle(main, { bot: basicDriver }) + let { bot: { selectResponses, dispose } } = sources run() - okTake(t, sources, (message) => { - t.ok(Message.is(Message(message)), 'message satisfies typecheck') + okTake(t, selectResponses({ responseType: Message }), dispose, (message) => { console.log(message) t.end() }) @@ -333,10 +337,10 @@ test.skip('should send video with basic driver', t => { ]) }) let { sources, run } = Cycle(main, { bot: basicDriver }) + let { bot: { selectResponses, dispose } } = sources run() - okTake(t, sources, (message) => { - t.ok(Message.is(Message(message)), 'message satisfies typecheck') + okTake(t, selectResponses({ responseType: Message }), dispose, (message) => { console.log(message) t.end() }) @@ -352,10 +356,10 @@ test.skip('should send voice with basic driver', t => { ]) }) let { sources, run } = Cycle(main, { bot: basicDriver }) + let { bot: { selectResponses, dispose } } = sources run() - okTake(t, sources, (message) => { - t.ok(Message.is(Message(message)), 'message satisfies typecheck') + okTake(t, selectResponses({ responseType: Message }), dispose, (message) => { console.log(message) t.end() }) @@ -371,10 +375,10 @@ test('should send location with basic driver', t => { ]) }) let { sources, run } = Cycle(main, { bot: basicDriver }) + let { bot: { selectResponses, dispose } } = sources run() - okTake(t, sources, (message) => { - t.ok(Message.is(Message(message)), 'message satisfies typecheck') + okTake(t, selectResponses({ responseType: Message }), dispose, (message) => { t.ok(message.hasOwnProperty('location'), 'message has property location') t.end() }) @@ -396,9 +400,10 @@ test('should send venue with basic driver', t => { ]) }) let { sources, run } = Cycle(main, { bot: basicDriver }) + let { bot: { selectResponses, dispose } } = sources run() - okTake(t, sources, (message) => { + okTake(t, selectResponses({ responseType: Message }), dispose, (message) => { t.ok(Message.is(Message(message)), 'message satisfies typecheck') t.ok(message.hasOwnProperty('venue'), 'message has property venue') t.equal(message.venue.title, 'Red Square', 'venue title should be Red Square') @@ -417,10 +422,10 @@ test('should send contact with basic driver', t => { ]) }) let { sources, run } = Cycle(main, { bot: basicDriver }) + let { bot: { selectResponses, dispose } } = sources run() - okTake(t, sources, (message) => { - t.ok(Message.is(Message(message)), 'message satisfies typecheck') + okTake(t, selectResponses({ responseType: Message }), dispose, (message) => { t.ok(message.hasOwnProperty('contact'), 'message has property contact') t.equal(message.contact.phone_number, '+42470', 'contact phone number should be +42470') t.equal(message.contact.first_name, 'Telegram', 'contact first name should be Telegram') @@ -438,9 +443,10 @@ test('should send chat action with basic driver', t => { ]) }) let { sources, run } = Cycle(main, { bot: basicDriver }) + let { bot: { responses, dispose } } = sources run() - okTake(t, sources, (bool) => { + okTake(t, responses, dispose, (bool) => { t.equal(bool, true, 'bool should be true') t.end() }) @@ -456,10 +462,10 @@ test('should get user profile photos with basic driver', t => { ]) }) let { sources, run } = Cycle(main, { bot: basicDriver }) + let { bot: { selectResponses, dispose } } = sources run() - okTake(t, sources, (userProfilePhotos) => { - t.ok(UserProfilePhotos.is(UserProfilePhotos(userProfilePhotos)), 'user profile photos satisfies typecheck') + okTake(t, selectResponses({ responseType: UserProfilePhotos }), dispose, (userProfilePhotos) => { t.ok(userProfilePhotos.hasOwnProperty('total_count'), 'user profile photos has property total_count') t.equal(typeof userProfilePhotos.total_count, 'number', 'total_count should be number') t.ok(userProfilePhotos.hasOwnProperty('photos'), 'user profile photos has property photos') @@ -478,9 +484,10 @@ test('should get file with basic driver', t => { ]) }) let { sources, run } = Cycle(main, { bot: basicDriver }) + let { bot: { selectResponses, dispose } } = sources run() - okTake(t, sources, (file) => { + okTake(t, selectResponses({ responseType: File }), dispose, (file) => { t.ok(File.is(File(file)), 'file satisfies typecheck') t.equal(file.file_id, FILE_ID, 'file ids should be equal') t.end() @@ -497,9 +504,10 @@ test('should kick chat member with basic driver', t => { ]) }) let { sources, run } = Cycle(main, { bot: basicDriver }) + let { bot: { responses, dispose } } = sources run() - okTake(t, sources, (bool) => { + okTake(t, responses, dispose, (bool) => { t.equal(bool, true, 'bool should be true') t.end() }) @@ -515,9 +523,10 @@ test('should unban chat member with basic driver', t => { ]) }) let { sources, run } = Cycle(main, { bot: basicDriver }) + let { bot: { responses, dispose } } = sources run() - okTake(t, sources, (bool) => { + okTake(t, responses, dispose, (bool) => { t.equal(bool, true, 'bool should be true') t.end() }) @@ -531,9 +540,10 @@ test('should get chat with basic driver', t => { ]) }) let { sources, run } = Cycle(main, { bot: basicDriver }) + let { bot: { selectResponses, dispose } } = sources run() - okTake(t, sources, (chat) => { + okTake(t, selectResponses({ responseType: Chat }), dispose, (chat) => { t.ok(Chat.is(Chat(chat)), 'chat satisfies typecheck') t.equal(chat.id, GROUP_ID) t.end() @@ -548,10 +558,10 @@ test('should get chat administrators with basic driver', t => { ]) }) let { sources, run } = Cycle(main, { bot: basicDriver }) + let { bot: { selectResponses, dispose } } = sources run() - okTake(t, sources, (chatMembers) => { - t.ok(tc.list(ChatMember).is(tc.list(ChatMember)(chatMembers)), 'chat members satisfies typecheck') + okTake(t, selectResponses({ responseType: tc.list(ChatMember) }), dispose, (chatMembers) => { chatMembers.forEach((chatMember: any) => { t.ok(chatMember.hasOwnProperty('user'), 'chat member has property user') t.ok(chatMember.hasOwnProperty('status'), 'chat member has property status') @@ -568,9 +578,10 @@ test('should get chat members count with basic driver', t => { ]) }) let { sources, run } = Cycle(main, { bot: basicDriver }) + let { bot: { responses, dispose } } = sources run() - okTake(t, sources, (chatMembersCount) => { + okTake(t, responses, dispose, (chatMembersCount) => { t.equal(typeof chatMembersCount, 'number') t.end() }) @@ -584,10 +595,10 @@ test('should get chat member with basic driver', t => { ]) }) let { sources, run } = Cycle(main, { bot: basicDriver }) + let { bot: { selectResponses, dispose } } = sources run() - okTake(t, sources, (chatMember) => { - t.ok(ChatMember.is(ChatMember(chatMember)), 'chat member satisfies typecheck') + okTake(t, selectResponses({ responseType: ChatMember }), dispose, (chatMember) => { t.ok(chatMember.hasOwnProperty('user'), 'chat member has property user') t.ok(chatMember.hasOwnProperty('status'), 'chat member has property status') t.equal(chatMember.user.id, 39759851, 'chat member id equals 39759851') @@ -619,9 +630,10 @@ test.skip('should answer callback query with basic driver', t => { ]) }) let { sources, run } = Cycle(main, { bot: basicDriver }) + let { bot: { responses, dispose } } = sources run() - okTake(t, sources, console.log.bind(console)) + okTake(t, responses, dispose, console.log.bind(console)) }) // Inline mode @@ -640,9 +652,10 @@ test('should edit message text with basic driver', t => { ]) }) let { sources, run } = Cycle(main, { bot: basicDriver }) + let { bot: { responses, dispose } } = sources run() - okDrop(t, sources, (message) => { + okDrop(t, responses, dispose, (message) => { if (typeof message !== 'boolean') { t.ok(Message.is(Message(message)), 'message satisfies typecheck') t.equal(message.text, 'Cycle.js') @@ -668,9 +681,10 @@ test('should edit message caption with basic driver', t => { ]) }) let { sources, run } = Cycle(main, { bot: basicDriver }) + let { bot: { responses, dispose } } = sources run() - okDrop(t, sources, (message) => { + okDrop(t, responses, dispose, (message) => { if (typeof message !== 'boolean') { t.ok(Message.is(Message(message)), 'message satisfies typecheck') t.equal(message.caption, 'Cycle.js') @@ -711,9 +725,10 @@ test('should edit message reply markup with basic driver', t => { ]) }) let { sources, run } = Cycle(main, { bot: basicDriver }) + let { bot: { responses, dispose } } = sources run() - okDrop(t, sources, (message) => { + okDrop(t, responses, dispose, (message) => { if (typeof message !== 'boolean') { t.ok(Message.is(Message(message)), 'message satisfies typecheck') t.equal(message.text, 'Message with reply_markup') @@ -742,9 +757,10 @@ test('should reply to inline query with basic driver', t => { ]) }) let { sources, run } = Cycle(main, { bot: basicDriver }) + let { bot: { responses, dispose } } = sources run() - okTake(t, sources, (bool) => { + okTake(t, responses, dispose, (bool) => { t.ok(bool, 'response should be truthy') t.end() }) @@ -758,9 +774,10 @@ test('should leave chat with basic driver', t => { ]) }) let { sources, run } = Cycle(main, { bot: basicDriver }) + let { bot: { responses, dispose } } = sources run() - okTake(t, sources, (bool) => { + okTake(t, responses, dispose, (bool) => { t.equal(bool, true, 'bool should be true') t.end() }) @@ -779,9 +796,10 @@ test('should send game with basic driver', t => { ]) }) let { sources, run } = Cycle(main, { bot: basicDriver }) + let { bot: { selectResponses, dispose } } = sources run() - okTake(t, sources, (message) => { + okTake(t, selectResponses({ responseType: Message }), dispose, (message) => { t.ok(Message.is(Message(message)), 'message satisfies typecheck') t.end() }) @@ -880,10 +898,10 @@ test('should get game high scores with basic driver', t => { ]) }) let { sources, run } = Cycle(main, { bot: basicDriver }) + let { bot: { selectResponses, dispose } } = sources run() - okDrop(t, sources, (gameHighScores) => { - t.ok(tc.list(GameHighScore).is(tc.list(GameHighScore)(gameHighScores)), 'game high scores satisfies typecheck') + okTake(t, selectResponses({ responseType: tc.list(GameHighScore) }), dispose, (gameHighScores) => { t.end() }) }) diff --git a/test/integration/plugins/most.ts b/test/integration/plugins/most.ts index 5d9a055..547e7d3 100644 --- a/test/integration/plugins/most.ts +++ b/test/integration/plugins/most.ts @@ -34,18 +34,20 @@ const test = tapeNock(tape, { }) const ACCESS_TOKEN = isRecord ? process.env['ACCESS_TOKEN'] : '123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11' -const onError: OnErrorFn = (sources, t) => (err) => { - sources.bot.dispose() +type Dispose = Sources['bot']['dispose'] + +const onError: OnErrorFn = (dispose, t) => (err) => { + dispose() console.error('test error: ', err) t.fail(err) t.end() } -const okTake: OkTakeFn = (t, sources, next, error = onError(sources, t)) => { - sources.bot.responses +const okTake: OkTakeFn = (t, source, dispose, next, error = onError(dispose, t)) => { + source .take(1) .subscribe({ - next (m: any) { - sources.bot.dispose() + next (m: T) { + dispose() next(m) }, error, @@ -80,9 +82,10 @@ test('should reply to command `/help` with basic driver', t => { ]) }) let { sources, run } = Cycle(main, { bot: basicDriver }) + let { bot: { selectResponses, dispose } } = sources run() - okTake(t, sources, (message) => { + okTake(t, selectResponses({ responseType: Message }), dispose, (message) => { t.ok(Message.is(Message(message)), 'message satisfies typecheck') t.ok( /\/(help)(?:@goodmind_test_bot)?(\s+(.+))?/.test(message.reply_to_message.text), diff --git a/test/integration/plugins/rxjs.ts b/test/integration/plugins/rxjs.ts index 44a01f4..ce397be 100644 --- a/test/integration/plugins/rxjs.ts +++ b/test/integration/plugins/rxjs.ts @@ -34,18 +34,20 @@ const test = tapeNock(tape, { }) const ACCESS_TOKEN = isRecord ? process.env['ACCESS_TOKEN'] : '123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11' -const onError: OnErrorFn = (sources, t) => (err) => { - sources.bot.dispose() +type Dispose = Sources['bot']['dispose'] + +const onError: OnErrorFn = (dispose, t) => (err) => { + dispose() console.error('test error: ', err) t.fail(err) t.end() } -const okTake: OkTakeFn = (t, sources, next, error = onError(sources, t)) => { - sources.bot.responses +const okTake: OkTakeFn = (t, source, dispose, next, error = onError(dispose, t)) => { + source .take(1) .subscribe( (m: T) => { - sources.bot.dispose() + dispose() next(m) }, error, @@ -78,10 +80,10 @@ test('should reply to command `/help` with basic driver', t => { ]) }) let { sources, run } = Cycle(main, { bot: basicDriver }) + let { bot: { selectResponses, dispose } } = sources run() - okTake(t, sources, (message) => { - t.ok(Message.is(Message(message)), 'message satisfies typecheck') + okTake(t, selectResponses({ responseType: Message }), dispose, (message) => { t.ok( /\/(help)(?:@goodmind_test_bot)?(\s+(.+))?/.test(message.reply_to_message.text), 'reply to message text should match `/help` command pattern') diff --git a/test/integration/plugins/xstream.ts b/test/integration/plugins/xstream.ts index 2b21e4c..9c3f388 100644 --- a/test/integration/plugins/xstream.ts +++ b/test/integration/plugins/xstream.ts @@ -34,18 +34,20 @@ const test = tapeNock(tape, { }) const ACCESS_TOKEN = isRecord ? process.env['ACCESS_TOKEN'] : '123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11' -const onError: OnErrorFn = (sources, t) => (err) => { - sources.bot.dispose() +type Dispose = Sources['bot']['dispose'] + +const onError: OnErrorFn = (dispose, t) => (err) => { + dispose() console.error('test error: ', err) t.fail(err) t.end() } -const okTake: OkTakeFn = (t, sources, next, error = onError(sources, t)) => { - sources.bot.responses +const okTake: OkTakeFn = (t, source, dispose, next, error = onError(dispose, t)) => { + source .take(1) .addListener({ next (m: T) { - sources.bot.dispose() + dispose() next(m) }, error, @@ -80,10 +82,10 @@ test('should reply to command `/help` with basic driver', t => { ]) }) let { sources, run } = Cycle(main, { bot: basicDriver }) + let { bot: { selectResponses, dispose } } = sources run() - okTake(t, sources, (message) => { - t.ok(Message.is(Message(message)), 'message satisfies typecheck') + okTake(t, selectResponses({ responseType: Message }), dispose, (message) => { t.ok( /\/(help)(?:@goodmind_test_bot)?(\s+(.+))?/.test(message.reply_to_message.text), 'reply to message text should match `/help` command pattern') diff --git a/test/interfaces.ts b/test/interfaces.ts index 1f2a54a..6c1fd8e 100644 --- a/test/interfaces.ts +++ b/test/interfaces.ts @@ -1,23 +1,23 @@ import tape from 'tape' +import X = require('../lib') -export interface OnErrorFn { - (sources: T, t: tape.Test): (err: any) => void -} +export type OnErrorFn = + (dispose: T, t: tape.Test) => (err: any) => void -export interface OkTakeFn { +export type OkTakeFn = ( t: tape.Test, - sources: T, + source: X.GenericStream, + dispose: Dispose, next: (m: U) => void, error?: (e: any) => void - ): void -} + ) => void -export interface OkDropFn { +export type OkDropFn = ( t: tape.Test, - sources: T, + source: X.GenericStream, + dispose: Dispose, next: (m: U) => void, error?: (e: any) => void - ): void -} + ) => void diff --git a/xstream-typings.d.ts b/xstream-typings.d.ts index 06343b0..086f814 100644 --- a/xstream-typings.d.ts +++ b/xstream-typings.d.ts @@ -6,6 +6,7 @@ declare module './lib' { token: Token updates: Stream responses: Stream + selectResponses (query: Partial<{ responseType: t.Type, method: string }>): Stream events (eventName: string): Stream dispose (): void }