forked from janeirodigital/sai-js
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Co-authored-by: elf Pavlik <[email protected]>
- Loading branch information
1 parent
7b50f2e
commit c19e961
Showing
6 changed files
with
335 additions
and
116 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
import type * as CliApp from '@effect/cli/CliApp'; | ||
import { NodeFileSystem, NodePath } from '@effect/platform-node'; | ||
import * as Console from 'effect/Console'; | ||
import * as Effect from 'effect/Effect'; | ||
import * as Fiber from 'effect/Fiber'; | ||
import * as Layer from 'effect/Layer'; | ||
import { describe, expect, it, vi } from 'vitest'; | ||
import * as MockConsole from './services/MockConsole'; | ||
import * as MockTerminal from './services/MockTerminal'; | ||
import { mainPrompt, SessionManager } from '../cli'; | ||
import { AuthorizationAgent } from '@janeirodigital/interop-authorization-agent'; | ||
|
||
const addSocialAgentRegistration = vi.fn(); | ||
|
||
const SessionManagerMock = Layer.succeed( | ||
SessionManager, | ||
SessionManager.of({ | ||
getSession: () => | ||
Effect.succeed({ | ||
registrySet: { | ||
hasAgentRegistry: { | ||
addSocialAgentRegistration | ||
} | ||
} | ||
} as unknown as AuthorizationAgent) | ||
}) | ||
); | ||
|
||
const MainLive = Effect.gen(function* () { | ||
const console = yield* MockConsole.make; | ||
return Layer.mergeAll(Console.setConsole(console), NodeFileSystem.layer, MockTerminal.layer, NodePath.layer); | ||
}).pipe(Layer.unwrapEffect); | ||
|
||
const runEffect = <E, A>(self: Effect.Effect<A, E, CliApp.CliApp.Environment>): Promise<A> => | ||
Effect.provide(self, MainLive).pipe(Effect.runPromise); | ||
|
||
describe('cli', () => { | ||
describe('mainPrompt', () => { | ||
it('should generate data registration', () => | ||
Effect.gen(function* () { | ||
const fiber = yield* Effect.fork(Effect.provide(mainPrompt, SessionManagerMock)); | ||
// select first account | ||
yield* MockTerminal.inputKey('enter'); | ||
// select first action | ||
yield* MockTerminal.inputKey('enter'); | ||
yield* MockTerminal.inputText('https://example.com/webid'); | ||
yield* MockTerminal.inputKey('enter'); | ||
yield* MockTerminal.inputText('alice'); | ||
yield* MockTerminal.inputKey('enter'); | ||
// note is empty | ||
yield* MockTerminal.inputKey('enter'); | ||
|
||
yield* Fiber.join(fiber); | ||
|
||
expect(addSocialAgentRegistration).toHaveBeenCalledWith('https://example.com/webid', 'alice', ''); | ||
}).pipe(runEffect)); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
/* eslint-disable */ | ||
// @ts-nocheck | ||
import * as Array from 'effect/Array'; | ||
import * as Console from 'effect/Console'; | ||
import * as Context from 'effect/Context'; | ||
import * as Effect from 'effect/Effect'; | ||
import * as Ref from 'effect/Ref'; | ||
|
||
export interface MockConsole extends Console.Console { | ||
readonly getLines: ( | ||
params?: Partial<{ | ||
readonly stripAnsi: boolean; | ||
}> | ||
) => Effect.Effect<ReadonlyArray<string>>; | ||
} | ||
|
||
export const MockConsole = Context.GenericTag<Console.Console, MockConsole>('effect/Console'); | ||
const pattern = new RegExp( | ||
[ | ||
'[\\u001B\\u009B][[\\]()#;?]*(?:(?:(?:(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]+)*|[a-zA-Z\\d]+(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]*)*)?\\u0007)', | ||
'(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PRZcf-ntqry=><~]))' | ||
].join('|'), | ||
'g' | ||
); | ||
|
||
const stripAnsi = (str: string) => str.replace(pattern, ''); | ||
|
||
export const make = Effect.gen(function* (_) { | ||
const lines = yield* _(Ref.make(Array.empty<string>())); | ||
|
||
const getLines: MockConsole['getLines'] = (params = {}) => | ||
Ref.get(lines).pipe(Effect.map((lines) => (params.stripAnsi || false ? Array.map(lines, stripAnsi) : lines))); | ||
|
||
const log: MockConsole['log'] = (...args) => Ref.update(lines, Array.appendAll(args)); | ||
|
||
return MockConsole.of({ | ||
[Console.TypeId]: Console.TypeId, | ||
getLines, | ||
log, | ||
unsafe: globalThis.console, | ||
assert: () => Effect.void, | ||
clear: Effect.void, | ||
count: () => Effect.void, | ||
countReset: () => Effect.void, | ||
debug: () => Effect.void, | ||
dir: () => Effect.void, | ||
dirxml: () => Effect.void, | ||
error: () => Effect.void, | ||
group: () => Effect.void, | ||
groupEnd: Effect.void, | ||
info: () => Effect.void, | ||
table: () => Effect.void, | ||
time: () => Effect.void, | ||
timeEnd: () => Effect.void, | ||
timeLog: () => Effect.void, | ||
trace: () => Effect.void, | ||
warn: () => Effect.void | ||
}); | ||
}); | ||
|
||
export const getLines = ( | ||
params?: Partial<{ | ||
readonly stripAnsi?: boolean; | ||
}> | ||
): Effect.Effect<ReadonlyArray<string>> => Effect.consoleWith((console) => (console as MockConsole).getLines(params)); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,101 @@ | ||
/* eslint-disable */ | ||
// @ts-nocheck | ||
import * as Terminal from '@effect/platform/Terminal'; | ||
import * as Array from 'effect/Array'; | ||
import * as Console from 'effect/Console'; | ||
import * as Context from 'effect/Context'; | ||
import * as Effect from 'effect/Effect'; | ||
import * as Layer from 'effect/Layer'; | ||
import * as Option from 'effect/Option'; | ||
import * as Queue from 'effect/Queue'; | ||
|
||
// ============================================================================= | ||
// Models | ||
// ============================================================================= | ||
|
||
export interface MockTerminal extends Terminal.Terminal { | ||
readonly inputText: (text: string) => Effect.Effect<void>; | ||
readonly inputKey: (key: string, modifiers?: Partial<MockTerminal.Modifiers>) => Effect.Effect<void>; | ||
} | ||
|
||
export declare namespace MockTerminal { | ||
export interface Modifiers { | ||
readonly ctrl: boolean; | ||
readonly meta: boolean; | ||
readonly shift: boolean; | ||
} | ||
} | ||
|
||
// ============================================================================= | ||
// Context | ||
// ============================================================================= | ||
|
||
export const MockTerminal = Context.GenericTag<Terminal.Terminal, MockTerminal>('@effect/platform/Terminal'); | ||
|
||
// ============================================================================= | ||
// Constructors | ||
// ============================================================================= | ||
|
||
export const make = Effect.gen(function* (_) { | ||
const queue = yield* _(Effect.acquireRelease(Queue.unbounded<Terminal.UserInput>(), Queue.shutdown)); | ||
|
||
const inputText: MockTerminal['inputText'] = (text: string) => { | ||
const inputs = Array.map(text.split(''), (key) => toUserInput(key)); | ||
return Queue.offerAll(queue, inputs).pipe(Effect.asVoid); | ||
}; | ||
|
||
const inputKey: MockTerminal['inputKey'] = (key: string, modifiers?: Partial<MockTerminal.Modifiers>) => { | ||
const input = toUserInput(key, modifiers); | ||
return Queue.offer(queue, input).pipe(Effect.asVoid); | ||
}; | ||
|
||
const display: MockTerminal['display'] = (input) => Console.log(input); | ||
|
||
const readInput: MockTerminal['readInput'] = Queue.take(queue).pipe( | ||
Effect.filterOrFail( | ||
(input) => !shouldQuit(input), | ||
() => new Terminal.QuitException() | ||
), | ||
Effect.timeoutFail({ | ||
duration: '2 seconds', | ||
onTimeout: () => new Terminal.QuitException() | ||
}) | ||
); | ||
|
||
return MockTerminal.of({ | ||
columns: Effect.succeed(80), | ||
display, | ||
readInput, | ||
readLine: Effect.succeed(''), | ||
inputKey, | ||
inputText | ||
}); | ||
}); | ||
|
||
// ============================================================================= | ||
// Layer | ||
// ============================================================================= | ||
|
||
export const layer = Layer.scoped(MockTerminal, make); | ||
|
||
// ============================================================================= | ||
// Accessors | ||
// ============================================================================= | ||
|
||
export const { columns, readInput, readLine } = Effect.serviceConstants(MockTerminal); | ||
export const { inputKey, inputText } = Effect.serviceFunctions(MockTerminal); | ||
|
||
// ============================================================================= | ||
// Utilities | ||
// ============================================================================= | ||
|
||
const shouldQuit = (input: Terminal.UserInput): boolean => | ||
input.key.ctrl && (input.key.name === 'c' || input.key.name === 'd'); | ||
|
||
const toUserInput = (key: string, modifiers: Partial<MockTerminal.Modifiers> = {}): Terminal.UserInput => { | ||
const { ctrl = false, meta = false, shift = false } = modifiers; | ||
return { | ||
input: Option.some(key), | ||
key: { name: key, ctrl, meta, shift } | ||
}; | ||
}; |
Oops, something went wrong.