Skip to content

Commit

Permalink
typescript wip
Browse files Browse the repository at this point in the history
  • Loading branch information
technoplato committed Apr 9, 2024
1 parent 7c6c1e6 commit 32e0bc3
Show file tree
Hide file tree
Showing 7 changed files with 155 additions and 100 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,6 @@ export const ActorSystemIds = {
features: 'featuresMachineId',
// Features
counting: 'countingMachineId',
/*not working atm*/ // countingPermissionReporter: 'countingPermissionReporterMachineId',
someFeature: 'someFeatureMachineId',
} as const;
Original file line number Diff line number Diff line change
Expand Up @@ -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: {
Expand All @@ -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,
Expand Down Expand Up @@ -101,8 +101,8 @@ export const countingMachineThatNeedsPermissionAt3 = setup({
},
},
invoke: {
id: 'permissionHandler',
systemId: 'countingPermissionReporter',
id: 'permissionReportingCounting',
systemId: 'permissionReportingCounting',
src: 'permissionReportingMachine',
input: ({ self }) => ({
permissions: [Permissions.bluetooth],
Expand Down
168 changes: 99 additions & 69 deletions libs/permissions/permissionLogic/src/lib/permission-logic.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { ActorSystemIds } from './application/actorIds';
import { createSkyInspector } from '@statelyai/inspect';
import { WebSocket } from 'ws';
import {
ActorSystem,
AnyActorRef,
createActor,
InspectionEvent,
Expand All @@ -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 () => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
import {
ActorRef,
assertEvent,
assign,
createMachine,
enqueueActions,
fromPromise,
log,
Expand All @@ -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({
Expand All @@ -26,8 +23,7 @@ export const permissionCheckerAndRequesterMachine = setup({
parent?: ActorRef<Snapshot<unknown>, PermissionMonitoringMachineEvents>;
statuses: PermissionStatusMapType;
},
// events: {} as PermissionMachineEvents,
// events : {} as PermissionCheckerAndRequesterMachineEvents,

input: {} as {
parent?: ActorRef<Snapshot<unknown>, PermissionMonitoringMachineEvents>;
},
Expand All @@ -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,
};
},
}),
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -164,7 +172,6 @@ export const permissionCheckerAndRequesterMachine = setup({
onDone: {
target: 'idle',
actions: [
log('child on done checkingPermissions'),
{
type: 'savePermissionCheckResult',
params: ({ event }) => ({
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
};
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import {
ActorRefFrom,
AnyActorRef,
assertEvent,
assign,
Expand All @@ -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 {
Expand All @@ -41,7 +31,6 @@ export const permissionMonitoringMachine = setup({
children: {
[ActorSystemIds.permissionCheckerAndRequester]: 'permissionCheckerAndRequesterMachine';
[ActorSystemIds.lifecycleReporting]: 'applicationLifecycleReportingMachine';
// [ActorSystemIds.features]: 'features';
};
},
actors: {
Expand Down Expand Up @@ -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;
Loading

0 comments on commit 32e0bc3

Please sign in to comment.