From 32e0bc30593e7748db5573f493d089bdd2f05cc9 Mon Sep 17 00:00:00 2001 From: Michael Lustig Date: Tue, 9 Apr 2024 10:51:26 -0400 Subject: [PATCH] typescript wip --- .../src/lib/application/actorIds.ts | 1 + .../lib/features/counting/counting.machine.ts | 6 +- .../src/lib/permission-logic.spec.ts | 168 +++++++++++------- .../permissionCheckAndRequestMachine.ts | 25 ++- .../monitoring/permissionMonitor.fixtures.ts | 14 ++ .../monitoring/permissionMonitor.machine.ts | 35 ++-- .../reporting/permissionReporting.machine.ts | 6 +- 7 files changed, 155 insertions(+), 100 deletions(-) create mode 100644 libs/permissions/permissionLogic/src/lib/permission/monitoring/permissionMonitor.fixtures.ts diff --git a/libs/permissions/permissionLogic/src/lib/application/actorIds.ts b/libs/permissions/permissionLogic/src/lib/application/actorIds.ts index f7db2cd..04c22aa 100644 --- a/libs/permissions/permissionLogic/src/lib/application/actorIds.ts +++ b/libs/permissions/permissionLogic/src/lib/application/actorIds.ts @@ -15,5 +15,6 @@ export const ActorSystemIds = { features: 'featuresMachineId', // Features counting: 'countingMachineId', + /*not working atm*/ // countingPermissionReporter: 'countingPermissionReporterMachineId', someFeature: 'someFeatureMachineId', } as const; diff --git a/libs/permissions/permissionLogic/src/lib/features/counting/counting.machine.ts b/libs/permissions/permissionLogic/src/lib/features/counting/counting.machine.ts index 856b825..9663d93 100644 --- a/libs/permissions/permissionLogic/src/lib/features/counting/counting.machine.ts +++ b/libs/permissions/permissionLogic/src/lib/features/counting/counting.machine.ts @@ -5,6 +5,7 @@ import { PermissionStatuses, } from '../../permission.types'; import { permissionReportingMachine } from '../../permission/reporting/permissionReporting.machine'; +import { ActorSystemIds } from '../../application/actorIds'; export const countingMachineThatNeedsPermissionAt3 = setup({ actors: { @@ -17,7 +18,6 @@ export const countingMachineThatNeedsPermissionAt3 = setup({ }).createMachine({ /** @xstate-layout N4IgpgJg5mDOIC5QGMD2BXAdgFwJaagEFMIAFMAJwFtdZZdVNYA6NLPA5sTAQwCMANpADEbHM3zIA2gAYAuolAAHVPTyNFIAB6IA7ACYANCACeiAIwBWc8xl27AZhm6rDpw4C+H42I5ES5NS09IwsvvhQXLyCIr4SmNLmCkggKmoMmJo6CPoAHA7M1pYAbM4AnLoOLpZGpoj61sxllg7mVZbN5gYALLle3iCYqBBwmuEExGSUNHQZ8ClpuOqZKdkAtPrdzMWWuboyZeZlZd1lDsUOxmYIG1v2Mm0uus1l+vpePhg4EZOBMyFMVhfPyaRbLLL1bpXRAXZinY4nCoOMoPGTdYofEDjfxTIKzUJA9gRKL8IQQUGqJYZCEIcz6GyWXTdfQHBo7GrQnI1OEtR4daw9PoDbG-abBOaE76cCC0UmQCnpDSrRA1XLMLplYq5Rn6E7o4qchr6HmtGTWc7dbqtTEigJi-GAgAWPBIAgif3FoQVVKVoGy+gKDm6dJRuTslhkF10nIcuTVEZkbhkuU1Vt0uW6NuBPzteIBLGdrvd9vzEggQm94OVCHT23ZZv2lUeBrqCG6umKtjs+mKums7e6JUzwuzE1z-wlhfLxbzEp4yDwADcwJXqdWXMwgyHk+HI1VOS0tgm8marMdzEKvEA */ type: 'parallel', - id: 'countingAndPermissions', context: { count: 0, permissionStatus: PermissionStatuses.unasked, @@ -101,8 +101,8 @@ export const countingMachineThatNeedsPermissionAt3 = setup({ }, }, invoke: { - id: 'permissionHandler', - systemId: 'countingPermissionReporter', + id: 'permissionReportingCounting', + systemId: 'permissionReportingCounting', src: 'permissionReportingMachine', input: ({ self }) => ({ permissions: [Permissions.bluetooth], diff --git a/libs/permissions/permissionLogic/src/lib/permission-logic.spec.ts b/libs/permissions/permissionLogic/src/lib/permission-logic.spec.ts index b70a306..1cb6f7e 100644 --- a/libs/permissions/permissionLogic/src/lib/permission-logic.spec.ts +++ b/libs/permissions/permissionLogic/src/lib/permission-logic.spec.ts @@ -4,6 +4,7 @@ import { ActorSystemIds } from './application/actorIds'; import { createSkyInspector } from '@statelyai/inspect'; import { WebSocket } from 'ws'; import { + ActorSystem, AnyActorRef, createActor, InspectionEvent, @@ -19,99 +20,128 @@ import { } from './permission.types'; import { permissionCheckerAndRequesterMachine } from './permission/checkAndRequest/permissionCheckAndRequestMachine'; import { - EmptyPermissionSubscriberMap, + PermissionMonitorActorRef, permissionMonitoringMachine, + PermissionMonitoringSnapshot, } from './permission/monitoring/permissionMonitor.machine'; import { someFeatureMachine } from './features/someFeature/someFeature.machine'; import { countingMachineThatNeedsPermissionAt3 } from './features/counting/counting.machine'; import { applicationMachine } from './application/application.machine'; +import { PermissionReportingActorRef } from './permission/reporting/permissionReporting.machine'; const vLongTime = 1000000000; +type JESActorSystem = ActorSystem<{ + actors: { + [ActorSystemIds.permissionMonitoring]: PermissionMonitorActorRef; + // [ActorSystemIds.countingPermissionReporter]: PermissionReportingActorRef; + }; +}>; + describe('Counting Machine That Needs Permission At 3', () => { - it('should increment count to 3, ask for permission, and continue counting to 5 when permission is granted', async () => { - const applicationActor = createActor(applicationMachine, { - systemId: ActorSystemIds.application, - // inspect: createSkyInspector({ - // // @ts-expect-error - // WebSocket: WebSocket, - // inspectorType: 'node', - // autoStart: true, - // }).inspect, - }); - applicationActor.start(); + it( + 'should increment count to 3, ask for permission, and continue counting to 5 when permission is granted', + async () => { + const applicationActor = createActor(applicationMachine, { + systemId: ActorSystemIds.application, + inspect: createSkyInspector({ + // @ts-expect-error + WebSocket: WebSocket, + inspectorType: 'node', + autoStart: true, + }).inspect, + }); + applicationActor.start(); + const actorSystem: JESActorSystem = applicationActor.system; + + const permissionMonitorActor = actorSystem.get( + ActorSystemIds.permissionMonitoring + ); + // const countingPermissionReporter = actorSystem.get( + // ActorSystemIds.countingPermissionReporter + // ); + const countingPermissionReporter = applicationActor.system.get( + 'permissionReportingCounting' + ); - const permissionMonitorActor = applicationActor.system.get( - ActorSystemIds.permissionMonitoring - ); + // @ts-expect-error this means the actor system type is working as expected + permissionMonitorActor.getSnapshot().value === 'foo'; + // @ts-expect-error this means the actor system type is working as expected + permissionMonitorActor.getSnapshot().context === 'foo'; - const countingPermissionReporter = applicationActor.system.get( - 'countingPermissionReporter' - ); + expect(permissionMonitorActor).toBeDefined(); + expect(countingPermissionReporter).toBeDefined(); - expect(permissionMonitorActor).toBeDefined(); - expect(countingPermissionReporter).toBeDefined(); + /* Required due to a bug in the initialization of actor systems*/ await new Promise( + (resolve) => setTimeout(resolve, 0) + ); - await new Promise((resolve) => setTimeout(resolve, 0)); + const state: PermissionMonitoringSnapshot = + permissionMonitorActor.getSnapshot(); - const state = permissionMonitorActor.getSnapshot(); - console.log({ - v: state.context.permissionSubscribers[Permissions.bluetooth][0].id, - }); - expect( - state.context.permissionSubscribers[Permissions.bluetooth]?.length - ).toEqual(1); + console.log({ + v: state.context.permissionSubscribers[Permissions.bluetooth].map( + (s) => s.id + ), + }); + expect( + state.context.permissionSubscribers[Permissions.bluetooth]?.length + ).toEqual(2); - const countingActor = applicationActor.system.get(ActorSystemIds.counting); - expect(countingActor?.getSnapshot().value).toStrictEqual({ - counting: 'enabled', - handlingPermissions: {}, - }); + const countingActor = applicationActor.system.get( + ActorSystemIds.counting + ); + expect(countingActor?.getSnapshot().value).toStrictEqual({ + counting: 'enabled', + handlingPermissions: {}, + }); - countingActor.send({ type: 'count.inc' }); - countingActor.send({ type: 'count.inc' }); - countingActor.send({ type: 'count.inc' }); - expect(countingActor.getSnapshot().context.count).toBe(3); - expect(countingActor.getSnapshot().value).toStrictEqual({ - counting: 'disabled', - handlingPermissions: {}, - }); + countingActor.send({ type: 'count.inc' }); + countingActor.send({ type: 'count.inc' }); + countingActor.send({ type: 'count.inc' }); + expect(countingActor.getSnapshot().context.count).toBe(3); + expect(countingActor.getSnapshot().value).toStrictEqual({ + counting: 'disabled', + handlingPermissions: {}, + }); - countingActor.send({ type: 'count.inc' }); - expect(countingActor.getSnapshot().context.count).toBe(3); - expect(countingActor.getSnapshot().value).toStrictEqual({ - counting: 'disabled', - handlingPermissions: {}, - }); + countingActor.send({ type: 'count.inc' }); + expect(countingActor.getSnapshot().context.count).toBe(3); + expect(countingActor.getSnapshot().value).toStrictEqual({ + counting: 'disabled', + handlingPermissions: {}, + }); - // Configure the permission actor to grant permission - const permissionCheckerActor = applicationActor.system.get( - ActorSystemIds.permissionCheckerAndRequester - ); + // Configure the permission actor to grant permission + const permissionCheckerActor = applicationActor.system.get( + ActorSystemIds.permissionCheckerAndRequester + ); - countingActor.send({ type: 'user.didTapBluetoothRequestPermission' }); + countingActor.send({ type: 'user.didTapBluetoothRequestPermission' }); - await waitFor(permissionCheckerActor, (state) => state.value === 'idle'); + await waitFor(permissionCheckerActor, (state) => state.value === 'idle'); + await new Promise((resolve) => setTimeout(resolve, vLongTime)); - expect(countingActor.getSnapshot().value).toStrictEqual({ - counting: 'enabled', - handlingPermissions: {}, - }); + expect(countingActor.getSnapshot().value).toStrictEqual({ + counting: 'enabled', + handlingPermissions: {}, + }); - expect(countingActor.getSnapshot().context.permissionStatus).toBe( - PermissionStatuses.granted - ); + expect(countingActor.getSnapshot().context.permissionStatus).toBe( + PermissionStatuses.granted + ); - // Send 'count.inc' events to increment the count to 5 - countingActor.send({ type: 'count.inc' }); - countingActor.send({ type: 'count.inc' }); + // Send 'count.inc' events to increment the count to 5 + countingActor.send({ type: 'count.inc' }); + countingActor.send({ type: 'count.inc' }); - expect(countingActor.getSnapshot().context.count).toBe(5); - expect(countingActor.getSnapshot().value.counting).toStrictEqual( - 'finished' - ); - // await new Promise((resolve) => setTimeout(resolve, vLongTime)); - }); + expect(countingActor.getSnapshot().context.count).toBe(5); + expect(countingActor.getSnapshot().value.counting).toStrictEqual( + 'finished' + ); + }, + vLongTime + ); // vLongTime // prettyMuchForever // it('should start in idle state', async () => { diff --git a/libs/permissions/permissionLogic/src/lib/permission/checkAndRequest/permissionCheckAndRequestMachine.ts b/libs/permissions/permissionLogic/src/lib/permission/checkAndRequest/permissionCheckAndRequestMachine.ts index bc8a834..edc8920 100644 --- a/libs/permissions/permissionLogic/src/lib/permission/checkAndRequest/permissionCheckAndRequestMachine.ts +++ b/libs/permissions/permissionLogic/src/lib/permission/checkAndRequest/permissionCheckAndRequestMachine.ts @@ -1,8 +1,6 @@ import { ActorRef, - assertEvent, assign, - createMachine, enqueueActions, fromPromise, log, @@ -17,7 +15,6 @@ import { PermissionStatus, PermissionStatusMapType, } from '../../permission.types'; -import { PermissionMachineEvents } from './permissionCheckAndRequest.types'; import { PermissionMonitoringMachineEvents } from '../monitoring/permissionMonitor.types'; export const permissionCheckerAndRequesterMachine = setup({ @@ -26,8 +23,7 @@ export const permissionCheckerAndRequesterMachine = setup({ parent?: ActorRef, PermissionMonitoringMachineEvents>; statuses: PermissionStatusMapType; }, - // events: {} as PermissionMachineEvents, - // events : {} as PermissionCheckerAndRequesterMachineEvents, + input: {} as { parent?: ActorRef, PermissionMonitoringMachineEvents>; }, @@ -48,10 +44,16 @@ export const permissionCheckerAndRequesterMachine = setup({ ), savePermissionRequestOutput: assign({ - statuses: ({ context, event }) => { + statuses: ( + { context }, + { + permission, + status, + }: { permission: Permission; status: PermissionStatus } + ) => { return { ...context.statuses, - [event.output.permission]: event.output.status, + [permission]: status, }; }, }), @@ -129,7 +131,13 @@ export const permissionCheckerAndRequesterMachine = setup({ onDone: { target: 'idle', actions: [ - 'savePermissionRequestOutput', + { + type: 'savePermissionRequestOutput', + params: ({ event }) => { + const { permission, status } = event.output; + return { permission, status }; + }, + }, { /** * I tried putting this action in the actions in setup as reportPermissionRequestResult @@ -164,7 +172,6 @@ export const permissionCheckerAndRequesterMachine = setup({ onDone: { target: 'idle', actions: [ - log('child on done checkingPermissions'), { type: 'savePermissionCheckResult', params: ({ event }) => ({ diff --git a/libs/permissions/permissionLogic/src/lib/permission/monitoring/permissionMonitor.fixtures.ts b/libs/permissions/permissionLogic/src/lib/permission/monitoring/permissionMonitor.fixtures.ts new file mode 100644 index 0000000..8a8e428 --- /dev/null +++ b/libs/permissions/permissionLogic/src/lib/permission/monitoring/permissionMonitor.fixtures.ts @@ -0,0 +1,14 @@ +import { PermissionSubscriberMap } from './permissionMonitor.types'; +import { Permissions, PermissionStatusMapType } from '../../permission.types'; + +export const EmptyPermissionSubscriberMap: PermissionSubscriberMap = + Object.values(Permissions).reduce( + (acc, permission) => ({ + ...acc, + }), + {} as PermissionSubscriberMap + ); +export type PermissionsMonitoringMachineContext = { + permissionsStatuses: PermissionStatusMapType; + permissionSubscribers: PermissionSubscriberMap; +}; diff --git a/libs/permissions/permissionLogic/src/lib/permission/monitoring/permissionMonitor.machine.ts b/libs/permissions/permissionLogic/src/lib/permission/monitoring/permissionMonitor.machine.ts index ba7d547..6621461 100644 --- a/libs/permissions/permissionLogic/src/lib/permission/monitoring/permissionMonitor.machine.ts +++ b/libs/permissions/permissionLogic/src/lib/permission/monitoring/permissionMonitor.machine.ts @@ -1,4 +1,5 @@ import { + ActorRefFrom, AnyActorRef, assertEvent, assign, @@ -7,32 +8,21 @@ import { raise, sendTo, setup, + SnapshotFrom, } from 'xstate'; import { ActorSystemIds } from '../../application/actorIds'; import { stubApplicationLifecycleReportingActorLogic } from '../../lifecycle/lifecycle.stubs'; import { InitialPermissionStatusMap } from '../../permission.fixtures'; -import { - Permission, - Permissions, - PermissionStatusMapType, -} from '../../permission.types'; +import { Permission } from '../../permission.types'; import { permissionCheckerAndRequesterMachine } from '../checkAndRequest/permissionCheckAndRequestMachine'; import { PermissionMonitoringMachineEvents, PermissionSubscriberMap, } from './permissionMonitor.types'; - -export const EmptyPermissionSubscriberMap: PermissionSubscriberMap = - Object.values(Permissions).reduce( - (acc, permission) => ({ - ...acc, - }), - {} as PermissionSubscriberMap - ); -export type PermissionsMonitoringMachineContext = { - permissionsStatuses: PermissionStatusMapType; - permissionSubscribers: PermissionSubscriberMap; -}; +import { + EmptyPermissionSubscriberMap, + PermissionsMonitoringMachineContext, +} from './permissionMonitor.fixtures'; export const permissionMonitoringMachine = setup({ types: {} as { @@ -41,7 +31,6 @@ export const permissionMonitoringMachine = setup({ children: { [ActorSystemIds.permissionCheckerAndRequester]: 'permissionCheckerAndRequesterMachine'; [ActorSystemIds.lifecycleReporting]: 'applicationLifecycleReportingMachine'; - // [ActorSystemIds.features]: 'features'; }; }, actors: { @@ -205,3 +194,13 @@ export const permissionMonitoringMachine = setup({ }, }, }); + +export type PermissionMonitorMachine = typeof permissionMonitoringMachine; +export type PermissionMonitorActorRef = ActorRefFrom< + typeof permissionMonitoringMachine +>; +export type PermissionMonitoringSnapshot = SnapshotFrom< + typeof permissionMonitoringMachine +>; +const a: PermissionMonitorActorRef = null as any; +const b: PermissionMonitoringSnapshot = null as any; diff --git a/libs/permissions/permissionLogic/src/lib/permission/reporting/permissionReporting.machine.ts b/libs/permissions/permissionLogic/src/lib/permission/reporting/permissionReporting.machine.ts index 7743f3f..404eaf9 100644 --- a/libs/permissions/permissionLogic/src/lib/permission/reporting/permissionReporting.machine.ts +++ b/libs/permissions/permissionLogic/src/lib/permission/reporting/permissionReporting.machine.ts @@ -1,4 +1,5 @@ import { + ActorRefFrom, AnyActorRef, AnyEventObject, enqueueActions, @@ -34,7 +35,6 @@ export const permissionReportingMachine = setup({ self, }) ), - // satisfies /*TODO type these events to the receiving machine event type*/ AnyEventObject); checkedSendParent: enqueueActions( ({ context, enqueue }, event: AnyEventObject) => { if (!context.parent) { @@ -122,3 +122,7 @@ export const permissionReportingMachine = setup({ }, }, }); + +export type PermissionReportingActorRef = ActorRefFrom< + typeof permissionReportingMachine +>;