提交 c2b4ab23 编写于 作者: Huan (李卓桓)'s avatar Huan (李卓桓)

add PuppetMock for unit tests & new puppet quickstart (#69 #70 #237 #400 #1016 #1095)

上级 c6959b1f
/**
* Wechaty - https://github.com/chatie/wechaty
*
* @copyright 2016-2018 Huan LI <zixia@zixia.net>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
import { PuppetMock } from './puppet-mock'
export {
PuppetMock,
}
export default PuppetMock
/**
* Wechaty - https://github.com/chatie/wechaty
*
* @copyright 2016-2018 Huan LI <zixia@zixia.net>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* @ignore
*/
import {
// config,
Sayable,
log,
} from '../config'
import {
Contact,
Gender,
} from '../puppet/contact'
// import PuppetMock from './puppet-mock'
import MockMessage from './mock-message'
export class MockContact extends Contact implements Sayable {
constructor(
public readonly id: string,
) {
super(id)
log.silly('MockContact', `constructor(${id})`)
}
public toString(): string {
return `MockContact<${this.id}>`
}
public async say(text: string): Promise<void>
public async say(message: MockMessage): Promise<void>
public async say(textOrMessage: string | MockMessage): Promise<void> {
log.verbose('MockContact', 'say(%s)', textOrMessage)
}
public name() {
return 'MockName'
}
public alias() : string | null
public alias(newAlias: string): Promise<void>
public alias(empty: null) : Promise<void>
public alias(newAlias?: string|null): Promise<void> | string | null {
if (newAlias === undefined) {
return 'MockAlias'
}
// pretend modified...
return Promise.resolve()
}
public stranger(): boolean | null {
return null
}
public official(): boolean {
return false
}
public personal(): boolean {
return !this.official()
}
public star(): boolean | null {
return null
}
public gender(): Gender {
return Gender.Unknown
}
public province() {
return 'Guangdong'
}
public city() {
return 'Shenzhen'
}
public async avatar(): Promise<NodeJS.ReadableStream> {
log.verbose('MockContact', 'avatar()')
throw new Error('To Be Mocked...')
}
public isReady(): boolean {
return true
}
public async refresh(): Promise<this> {
return this
}
public async ready(): Promise<this> {
return this
}
public self(): boolean {
return false
}
public weixin(): string | null {
return null
}
}
export default MockContact
/**
* Wechaty - https://github.com/chatie/wechaty
*
* @copyright 2016-2018 Huan LI <zixia@zixia.net>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* @ignore
*/
import {
// config,
log,
} from '../config'
import FriendRequest from '../puppet/friend-request'
import MockContact from './mock-contact'
export class MockFriendRequest extends FriendRequest {
public info: any
private ticket: string
constructor() {
log.verbose('MockFriendRequest', 'constructor()')
super()
}
public receive(info: any): void {
log.verbose('MockFriendRequest', 'receive(%s)', info)
}
public confirm(contact: MockContact): void {
log.verbose('MockFriendRequest', 'confirm(%s)', contact)
}
public async send(contact: MockContact, hello = 'Hi'): Promise<void> {
log.verbose('MockFriendRequest', 'send(%s)', contact)
await this.puppet.friendRequestSend(contact, hello)
}
public async accept(): Promise<void> {
log.verbose('FriendRequest', 'accept() %s', this.contact)
await this.puppet.friendRequestAccept(this.contact, this.ticket)
}
}
export default MockFriendRequest
/**
* Wechaty - https://github.com/chatie/wechaty
*
* @copyright 2016-2018 Huan LI <zixia@zixia.net>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* @ignore
*/
import * as path from 'path'
import {
Readable,
} from 'stream'
import {
log,
} from '../config'
import Message from '../puppet/message'
import MockContact from './mock-contact'
import WebRoom from './mock-room'
import {
MsgRawObj,
MsgType,
AppMsgType,
} from '../puppet-web/schema'
export type ParsedPath = Partial<path.ParsedPath>
export class MockMessage extends Message {
public readonly id: string
constructor(
fileOrObj?: string | MsgRawObj,
) {
super()
log.silly('MockMessage', 'constructor()')
}
public toString() {
return `MockMessage`
}
public from(contact: MockContact): void
public from(id: string): void
public from(): MockContact
public from(contact?: MockContact|string): MockContact|void {
if (contact) {
return
}
const loadedContact = MockContact.load('mockid') as MockContact
loadedContact.puppet = this.puppet
return loadedContact
}
public room(room: WebRoom): void
public room(id: string): void
public room(): WebRoom|null
public room(room?: WebRoom|string): WebRoom|null|void {
if (room) {
return
}
return null
}
public text(): string
public text(content: string): void
public text(text?: string): string | void {
if (text) {
return
}
return 'mock text'
}
public async say(textOrMessage: string | MockMessage, replyTo?: MockContact|MockContact[]): Promise<void> {
log.verbose('MockMessage', 'say(%s, %s)', textOrMessage, replyTo)
const m = new MockMessage()
await this.puppet.send(m)
}
public type(): MsgType {
return MsgType.TEXT
}
public self(): boolean {
const userId = this.puppet.user!.id
const fromId = this.from().id
if (!userId || !fromId) {
throw new Error('no user or no from')
}
return fromId === userId
}
public mentioned(): MockContact[] {
return []
}
public async ready(): Promise<this> {
log.silly('MockMessage', 'ready()')
await this.readyMedia()
return this
}
public async readyMedia(): Promise<this> {
log.silly('MockMessage', 'readyMedia()')
return this
}
public static async find(query) {
return Promise.resolve(new MockMessage(<MsgRawObj>{MsgId: '-1'}))
}
public static async findAll(query) {
return Promise.resolve([
new MockMessage (<MsgRawObj>{MsgId: '-2'}),
new MockMessage (<MsgRawObj>{MsgId: '-3'}),
])
}
public to(contact: MockContact): void
public to(id: string): void
public to(): MockContact | null // if to is not set, then room must had set
public to(contact?: MockContact | string): MockContact | WebRoom | null | void {
if (contact) {
return
}
const to = MockContact.load('mockid') as MockContact
to.puppet = this.puppet
return to
}
public async readyStream(): Promise<Readable> {
log.verbose('MockMessage', 'readyStream()')
throw new Error('to be mocked')
}
public filename(): string {
return 'mocked_filename.txt'
}
public ext(): string {
return '.mocked_ext'
}
public mimeType(): string | null {
return 'text/plain'
}
public async forward(to: WebRoom|MockContact): Promise<void> {
/**
* 1. Text message
*/
if (this.type() === MsgType.TEXT) {
await to.say(this.text())
return
}
/**
* 2. Media message
*/
try {
await this.puppet.forward(this, to)
} catch (e) {
log.error('MockMessage', 'forward(%s) exception: %s', to, e)
throw e
}
}
public typeSub(): MsgType {
return MsgType.TEXT
}
public typeApp(): AppMsgType {
return AppMsgType.TEXT
}
}
export default MockMessage
/**
* Wechaty - https://github.com/chatie/wechaty
*
* @copyright 2016-2018 Huan LI <zixia@zixia.net>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* @ignore
*/
import {
log,
} from '../config'
import {
Room,
RoomMemberQueryFilter,
} from '../puppet/'
import MockMessage from './mock-message'
import MockContact from './mock-contact'
export class MockRoom extends Room {
constructor(
public id: string,
) {
super(id)
log.silly('MockRoom', `constructor(${id})`)
}
public isReady(): boolean {
return true
}
public async ready(): Promise<Room> {
return this
}
public say(message: MockMessage) : Promise<void>
public say(text: string) : Promise<void>
public say(text: string, replyTo: MockContact) : Promise<void>
public say(text: string, replyTo: MockContact[]) : Promise<void>
public say(text: never, ...args: never[]) : Promise<never>
public async say(textOrMessage: string | MockMessage, replyTo?: MockContact|MockContact[]): Promise<void> {
log.verbose('MockRoom', 'say(%s, %s)',
textOrMessage,
Array.isArray(replyTo)
? replyTo.map(c => c.name()).join(', ')
: replyTo ? replyTo.name() : '',
)
let m
m = new MockMessage()
m.puppet = this.puppet
m.room(this)
await this.puppet.send(m)
}
public async add(contact: MockContact): Promise<void> {
log.verbose('MockRoom', 'add(%s)', contact)
await this.puppet.roomAdd(this, contact)
}
public async del(contact: MockContact): Promise<void> {
log.verbose('MockRoom', 'del(%s)', contact.name())
await this.puppet.roomDel(this, contact)
}
public async quit(): Promise<void> {
return
}
public topic() : string
public async topic(newTopic: string): Promise<void>
public topic(newTopic?: string): string | Promise<void> {
log.verbose('MockRoom', 'topic(%s)', newTopic ? newTopic : '')
if (typeof newTopic === 'undefined') {
return 'mock topic'
}
this.puppet.roomTopic(this, newTopic)
.catch(e => {
log.warn('MockRoom', 'topic(newTopic=%s) exception: %s',
newTopic, e && e.message || e,
)
})
return Promise.resolve()
}
public alias(contact: MockContact): string | null {
return this.roomAlias(contact)
}
public roomAlias(contact: MockContact): string | null {
return 'mock room alias'
}
public has(contact: MockContact): boolean {
return false
}
public memberAll(filter: RoomMemberQueryFilter) : MockContact[]
public memberAll(name: string) : MockContact[]
public memberAll(queryArg: RoomMemberQueryFilter | string): MockContact[] {
return []
}
public member(name: string) : MockContact | null
public member(filter: RoomMemberQueryFilter): MockContact | null
public member(queryArg: RoomMemberQueryFilter | string): MockContact | null {
log.verbose('MockRoom', 'member(%s)', JSON.stringify(queryArg))
return null
}
public memberList(): MockContact[] {
log.verbose('MockRoom', 'memberList')
return []
}
public static async create(contactList: MockContact[], topic?: string): Promise<Room> {
log.verbose('MockRoom', 'create(%s, %s)', contactList.join(','), topic)
const room = await this.puppet.roomCreate(contactList, topic)
return room
}
public async refresh(): Promise<void> {
return
}
public owner(): MockContact | null {
log.info('MockRoom', 'owner()')
return null
}
}
export default MockRoom
/**
* Wechaty - https://github.com/chatie/wechaty
*
* @copyright 2016-2018 Huan LI <zixia@zixia.net>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
import {
ContactQueryFilter,
Puppet,
PuppetOptions,
RoomQueryFilter,
} from '../puppet/'
import {
log,
} from '../config'
import {
} from '../message'
import MockContact from './mock-contact'
import MockMessage from './mock-message'
import MockRoom from './mock-room'
export type PuppetFoodType = 'scan' | 'ding'
export type ScanFoodType = 'scan' | 'login' | 'logout'
export class PuppetMock extends Puppet {
constructor(
public options: PuppetOptions,
) {
super(options)
}
public toString() {
return `PuppetMock<${this.options.profile.name}>`
}
public ding(data?: any): Promise<string> {
return data
}
public async start(): Promise<void> {
log.verbose('PuppetMock', `start() with ${this.options.profile}`)
this.state.on('pending')
// await some tasks...
this.state.on(true)
}
public async stop(): Promise<void> {
log.verbose('PuppetMock', 'quit()')
if (this.state.off()) {
log.warn('PuppetMock', 'quit() is called on a OFF puppet. await ready(off) and return.')
await this.state.ready('off')
return
}
this.state.off('pending')
// await some tasks...
this.state.off(true)
}
public logonoff(): boolean {
return !!(this.user)
}
public self(): MockContact {
log.verbose('PuppetMock', 'self()')
if (this.user) {
return this.user as MockContact
}
throw new Error('PuppetMock.self() no this.user')
}
public async forward(message: MockMessage, sendTo: MockContact | MockRoom): Promise<void> {
log.silly('PuppetMock', 'forward() to: %s, message: %s)',
sendTo, message.filename(),
// patchData.ToUserName,
// patchData.MMActualContent,
)
}
public async send(message: MockMessage): Promise<void> {
//
}
public async say(text: string): Promise<void> {
if (!this.logonoff()) {
throw new Error('can not say before login')
}
if (!text) {
log.warn('PuppetMock', 'say(%s) can not say nothing', text)
return
}
if (!this.user) {
log.warn('PuppetMock', 'say(%s) can not say because no user', text)
this.emit('error', new Error('no this.user for PuppetMock'))
return
}
return await this.user.say(text)
}
public async logout(): Promise<void> {
log.verbose('PuppetMock', 'logout()')
this.user = undefined
}
public contactAlias(contact: MockContact) : Promise<string>
public contactAlias(contact: MockContact, alias: string | null): Promise<void>
public async contactAlias(contact: MockContact, alias?: string|null): Promise<void | string> {
if (typeof alias === 'undefined') {
return 'mock alias'
}
return
}
public async contactFindAll(query: ContactQueryFilter): Promise<MockContact[]> {
return []
}
public async roomFindAll(
query: RoomQueryFilter = { topic: /.*/ },
): Promise<MockRoom[]> {
return []
}
public async roomDel(
room: MockRoom,
contact: MockContact,
): Promise<void> {
//
}
public async roomAdd(
room: MockRoom,
contact: MockContact,
): Promise<void> {
//
}
public async roomTopic(room: MockRoom, topic?: string): Promise<void | string> {
if (typeof topic === 'undefined') {
return 'mock room topic'
}
return
}
public async roomCreate(contactList: MockContact[], topic: string): Promise<MockRoom> {
if (!contactList || ! contactList.map) {
throw new Error('contactList not found')
}
const r = MockRoom.load('mock room id') as MockRoom
r.puppet = this
return r
}
public async friendRequestSend(contact: MockContact, hello: string): Promise<void> {
//
}
public async friendRequestAccept(contact: MockContact, ticket: string): Promise<void> {
//
}
}
export default PuppetMock
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册