diff --git a/projects/ppwcode/ng-resource/src/lib/wrappers/ppw-resource-execution.spec.ts b/projects/ppwcode/ng-resource/src/lib/wrappers/ppw-resource-execution.spec.ts index 873ea9d6..20300c09 100644 --- a/projects/ppwcode/ng-resource/src/lib/wrappers/ppw-resource-execution.spec.ts +++ b/projects/ppwcode/ng-resource/src/lib/wrappers/ppw-resource-execution.spec.ts @@ -1,5 +1,5 @@ import { HttpErrorResponse } from '@angular/common/http' -import { ResourceSnapshot, resourceFromSnapshots, signal } from '@angular/core' +import { resourceFromSnapshots, ResourceSnapshot, signal } from '@angular/core' import { TestBed } from '@angular/core/testing' import { PPW_RESOURCE_ERROR_EXTRACTOR } from '../error-handling/extractor' import { PpwResourceExecution, PpwResourceExecutionOptions } from './ppw-resource-execution' @@ -203,6 +203,33 @@ describe('PpwResourceExecution state', () => { expect(() => vi.runOnlyPendingTimers()).not.toThrow() }) + it('should resolve the promise with the resource value after a successful execution', async () => { + const { resource, snapshot } = createFakeHttpResource(idleSnapshot()) + const execution = createExecution(resource) + const promise = execution.toPromise() + + snapshot.set(resolvedSnapshot('result')) + TestBed.tick() + + vi.runOnlyPendingTimers() + + await expect(promise).resolves.toBe('result') + }) + + it('should reject the promise with the resource error after a failed execution', async () => { + const { resource, snapshot } = createFakeHttpResource(idleSnapshot()) + const execution = createExecution(resource) + const error = new Error('Failed') + const promise = execution.toPromise() + + snapshot.set(errorSnapshot(error)) + TestBed.tick() + + vi.runOnlyPendingTimers() + + await expect(promise).rejects.toBe(error) + }) + it('should not rerun success callbacks when only the exposed value changes', () => { const { resource, snapshot } = createFakeHttpResource(idleSnapshot()) const onSuccess = vi.fn<(value: Exclude) => void>() diff --git a/projects/ppwcode/ng-resource/src/lib/wrappers/ppw-resource-execution.ts b/projects/ppwcode/ng-resource/src/lib/wrappers/ppw-resource-execution.ts index dc4f7adb..2684da02 100644 --- a/projects/ppwcode/ng-resource/src/lib/wrappers/ppw-resource-execution.ts +++ b/projects/ppwcode/ng-resource/src/lib/wrappers/ppw-resource-execution.ts @@ -158,6 +158,37 @@ export class PpwResourceExecution { this.#completionHandlers.add(handler) } + /** + * Converts the current operation into a Promise that resolves or rejects + * based on the operation's completion state. + * + * @return {Promise} A Promise that resolves with the result of + * the operation if successful, or rejects with the error if it fails. + */ + public toPromise(): Promise { + // Start by instantiating a new Promise to be returned at the end, so we can capture the resolve and reject callbacks. + let resolve: (value: TResult) => void + let reject: (reason?: unknown) => void + + const promise = new Promise((res, rej) => { + resolve = res + reject = rej + }) + + // Register a completion handler that will be triggered when the resource state changes to resolved or error. + // This handler will be invoked with a boolean indicating whether the call was successful, allowing us to + // invoke the captured resolve or reject Promise callbacks with either the resource value or error. + this.#completionHandlers.add((hasFailed) => { + if (hasFailed) { + reject(this.error()) + } else { + resolve(this.value()) + } + }) + + return promise + } + /** * Starts a set of optional executions and reports their aggregate result. *