Skip to content

Commit

Permalink
fix(active-account): also set value if promise is not settled
Browse files Browse the repository at this point in the history
  • Loading branch information
AndreasGassmann committed Jun 9, 2020
1 parent 4a68db7 commit 89f299b
Show file tree
Hide file tree
Showing 4 changed files with 57 additions and 32 deletions.
4 changes: 3 additions & 1 deletion src/clients/client/Client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,9 @@ export abstract class Client extends BeaconClient {
private async setTransport(transport: Transport): Promise<void> {
if (this._transport.isSettled()) {
// If the promise has already been resolved we need to create a new one.
this._transport = new ExposedPromise({ result: transport })
this._transport = ExposedPromise.resolve(transport)
} else {
this._transport.resolve(transport)
}

await this.events.emit(BeaconEvent.ACTIVE_TRANSPORT_SET, transport)
Expand Down
4 changes: 3 additions & 1 deletion src/clients/dapp-client/DAppClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,9 @@ export class DAppClient extends Client {
public async setActiveAccount(account?: AccountInfo): Promise<void> {
if (this._activeAccount.isSettled()) {
// If the promise has already been resolved we need to create a new one.
this._activeAccount = new ExposedPromise<AccountInfo | undefined>({ result: account })
this._activeAccount = ExposedPromise.resolve<AccountInfo | undefined>(account)
} else {
this._activeAccount.resolve(account)
}

await this.storage.set(
Expand Down
30 changes: 15 additions & 15 deletions src/utils/exposed-promise.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,11 @@ export enum ExposedPromiseStatus {
type Resolve<T> = (value?: T) => void
type Reject<U> = (reason?: U) => void

interface ExposedPromiseOptions<T, U = unknown> {
result?: T
error?: U
}

const notInitialized = (): never => {
throw new Error('ExposedPromise not initialized yet.')
}

export class ExposedPromise<T, U = unknown> {
export class ExposedPromise<T = unknown, U = unknown> {
private readonly _promise: Promise<T>

private _resolve: Resolve<T> = notInitialized
Expand Down Expand Up @@ -45,7 +40,7 @@ export class ExposedPromise<T, U = unknown> {
return this._promiseError
}

constructor(options?: ExposedPromiseOptions<T, U>) {
constructor() {
this._promise = new Promise<T>((innerResolve: Resolve<T>, innerReject: Reject<U>): void => {
this._resolve = (value?: T): void => {
if (this.isSettled()) {
Expand Down Expand Up @@ -74,15 +69,20 @@ export class ExposedPromise<T, U = unknown> {
return
}
})
}

// Immediately resolve the promise if result or error have been passed
if (options && typeof options === 'object') {
if (options.hasOwnProperty('result')) {
this._resolve(options.result)
} else if (options.hasOwnProperty('error')) {
this._reject(options.error)
}
}
public static resolve<T>(value?: T): ExposedPromise<T> {
const promise = new ExposedPromise<T>()
promise.resolve(value)

return promise
}

public static reject<T = never, U = unknown>(reason?: U): ExposedPromise<T, U> {
const promise = new ExposedPromise<T, U>()
promise.reject(reason)

return promise
}

public isPending(): boolean {
Expand Down
51 changes: 36 additions & 15 deletions test/utils/exposed-promise.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ const cancelTimeoutAndSettle = (
}

const getExpectedPromiseOutcome = (
exposed: ExposedPromise<any>,
exposed: ExposedPromise<any, any>,
expectedStatus: ExposedPromiseStatus
) => {
const resolvePredicate = expectedStatus === ExposedPromiseStatus.RESOLVED
Expand Down Expand Up @@ -95,29 +95,49 @@ describe.only(`ExposedPromise`, () => {
expect(await Promise.all(promises)).to.deep.equal([undefined, undefined, undefined])
})

it(`should create an empty ExposedPromise when passing an empty object`, async () => {
const exposed = new ExposedPromise({})
it(`should correctly resolve an ExposedPromise`, async () => {
const exposed = new ExposedPromise()
const successMessage = 'success message!'
exposed.resolve(successMessage)

const promises = getExpectedPromiseOutcome(exposed, ExposedPromiseStatus.PENDING)
const promises = getExpectedPromiseOutcome(exposed, ExposedPromiseStatus.RESOLVED)

expect(exposed instanceof ExposedPromise, 'is instanceof ExposedPromise').to.be.true
expect(exposed.isPending(), 'isPending').to.be.true
expect(exposed.isResolved(), 'isResolved').to.be.false
expect(exposed.isPending(), 'isPending').to.be.false
expect(exposed.isResolved(), 'isResolved').to.be.true
expect(exposed.isRejected(), 'isRejected').to.be.false
expect(exposed.isSettled(), 'isSettled').to.be.false
expect(exposed.isSettled(), 'isSettled').to.be.true
expect(typeof exposed.promise, 'promise').to.equal('object')
expect(exposed.promiseResult, 'promiseResult').to.equal(successMessage)
expect(exposed.promiseError, 'promiseError').to.be.undefined
expect(exposed.status, 'status').to.equal(ExposedPromiseStatus.RESOLVED)
expect(typeof exposed.resolve, 'resolve').to.equal('function')
expect(typeof exposed.reject, 'reject').to.equal('function')
expect(await Promise.all(promises)).to.deep.equal(['success message!', undefined, undefined])
})

it(`should create a resolved ExposedPromise when calling the static resolve`, async () => {
const exposed = ExposedPromise.resolve()

const promises = getExpectedPromiseOutcome(exposed, ExposedPromiseStatus.RESOLVED)

expect(exposed instanceof ExposedPromise, 'is instanceof ExposedPromise').to.be.true
expect(exposed.isPending(), 'isPending').to.be.false
expect(exposed.isResolved(), 'isResolved').to.be.true
expect(exposed.isRejected(), 'isRejected').to.be.false
expect(exposed.isSettled(), 'isSettled').to.be.true
expect(typeof exposed.promise, 'promise').to.equal('object')
expect(exposed.promiseResult, 'promiseResult').to.be.undefined
expect(exposed.promiseError, 'promiseError').to.be.undefined
expect(exposed.status, 'status').to.equal(ExposedPromiseStatus.PENDING)
expect(exposed.status, 'status').to.equal(ExposedPromiseStatus.RESOLVED)
expect(typeof exposed.resolve, 'resolve').to.equal('function')
expect(typeof exposed.reject, 'reject').to.equal('function')
expect(await Promise.all(promises)).to.deep.equal([undefined, undefined, undefined])
})

it(`should correctly resolve an ExposedPromise`, async () => {
const exposed = new ExposedPromise()
it(`should create a resolved ExposedPromise when calling the static resolve with data`, async () => {
const successMessage = 'success message!'
exposed.resolve(successMessage)
const exposed = ExposedPromise.resolve(successMessage)

const promises = getExpectedPromiseOutcome(exposed, ExposedPromiseStatus.RESOLVED)

Expand Down Expand Up @@ -181,7 +201,7 @@ describe.only(`ExposedPromise`, () => {

it(`should resolve options.result immediately`, async () => {
const successMessage = 'success message!'
const exposed = new ExposedPromise({ result: successMessage })
const exposed = ExposedPromise.resolve(successMessage)

const promises = getExpectedPromiseOutcome(exposed, ExposedPromiseStatus.RESOLVED)

Expand All @@ -201,7 +221,7 @@ describe.only(`ExposedPromise`, () => {

it(`should resolve options.result (undefined) immediately`, async () => {
const successMessage = undefined
const exposed = new ExposedPromise({ result: successMessage })
const exposed = ExposedPromise.resolve(successMessage)

const promises = getExpectedPromiseOutcome(exposed, ExposedPromiseStatus.RESOLVED)

Expand Down Expand Up @@ -270,7 +290,7 @@ describe.only(`ExposedPromise`, () => {
it(`should reject options.reject immediately`, async () => {
const failureMessage = 'failure message!'

const exposed = new ExposedPromise<string, unknown>({ error: failureMessage })
const exposed = ExposedPromise.reject<unknown>(failureMessage)
exposed.promise.catch(() => undefined) // We need to add a catch here or it will trigger an unhandled rejection error

const promises = getExpectedPromiseOutcome(exposed, ExposedPromiseStatus.REJECTED)
Expand All @@ -288,10 +308,11 @@ describe.only(`ExposedPromise`, () => {
expect(typeof exposed.reject, 'reject').to.equal('function')
expect(await Promise.all(promises)).to.deep.equal([undefined, 'failure message!', undefined])
})

it(`should reject options.reject (undefined) immediately`, async () => {
const failureMessage = 'failure message!'

const exposed = new ExposedPromise<string, unknown>({ error: failureMessage })
const exposed = ExposedPromise.reject<unknown>(failureMessage)
exposed.promise.catch(() => undefined) // We need to add a catch here or it will trigger an unhandled rejection error

const promises = getExpectedPromiseOutcome(exposed, ExposedPromiseStatus.REJECTED)
Expand Down

0 comments on commit 89f299b

Please sign in to comment.