Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 66 additions & 2 deletions app/src/components/admin/ComplianceSection.test.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
import { fireEvent, render, screen, within } from "@testing-library/react"
import { beforeEach, describe, expect, it, type Mock, vi } from "vitest"
import { act, fireEvent, render, screen, within } from "@testing-library/react"
import {
afterEach,
beforeEach,
describe,
expect,
it,
type Mock,
vi,
} from "vitest"
import { type Grant } from "../../hooks/useAdminAuthority"
import { type TransactionStatus } from "../../hooks/useContractTransaction"
import ComplianceSection from "./ComplianceSection"
Expand All @@ -8,10 +16,12 @@ const { txs, useComplianceStateMock } = vi.hoisted(() => {
const tx = (): {
status: TransactionStatus
submit: Mock<() => boolean>
prepare: Mock
reset: Mock
} => ({
status: { status: "idle" },
submit: vi.fn(() => true),
prepare: vi.fn(),
reset: vi.fn(),
})
return {
Expand Down Expand Up @@ -95,6 +105,10 @@ const readsFor = ({ target }: { target: string | undefined }) => ({
})

describe("ComplianceSection", () => {
afterEach(() => {
vi.useRealTimers()
})

beforeEach(() => {
vi.clearAllMocks()
for (const transaction of Object.values(txs))
Expand Down Expand Up @@ -220,6 +234,56 @@ describe("ComplianceSection", () => {
).toBe(false)
})

it("prepares allowing the target once it settles and still submits it on click", () => {
vi.useFakeTimers()
render(
<ComplianceSection
grants={[verifierAdmin]}
wallet={WALLET}
addresses={ADDRESSES}
/>,
)
enterTarget(TARGET)
openRow("Allow the address")

act(() => {
vi.advanceTimersByTime(400)
})

const allowArg = { verifier: "CVERIFIERADDRESS", account: TARGET }
expect(txs.useAllowAddress.prepare).toHaveBeenCalledWith(
allowArg,
verifierAdmin,
)

fireEvent.click(button("Allow the address"))

expect(txs.useAllowAddress.submit).toHaveBeenCalledWith(
allowArg,
verifierAdmin,
)
})

it("prepares nothing while the compliance tab is hidden", () => {
vi.useFakeTimers()
render(
<ComplianceSection
grants={[verifierAdmin]}
wallet={WALLET}
addresses={ADDRESSES}
active={false}
/>,
)
enterTarget(TARGET)
openRow("Allow the address")

act(() => {
vi.advanceTimersByTime(400)
})

expect(txs.useAllowAddress.prepare).not.toHaveBeenCalled()
})

it("allows the target and follows it in the modal", () => {
const { rerender } = render(
<ComplianceSection
Expand Down
28 changes: 19 additions & 9 deletions app/src/components/admin/ComplianceSection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ import {
useUnfreezeShares,
} from "../../hooks/useComplianceTransactions"
import {
prepareFor,
type Send,
type Sendable,
submitTo,
usePendingTransaction,
type UsePendingTransaction,
} from "../../hooks/usePendingTransaction"
Expand Down Expand Up @@ -65,24 +69,25 @@ function useComplianceFlows() {

type Flows = ReturnType<typeof useComplianceFlows>

const submitArg = <Arg,>(
{ submit }: { submit: (arg: Arg, label: string, grant?: Grant) => boolean },
const sendArg = <Arg,>(
send: Send,
flow: Sendable<Arg>,
{ arg, next, grant }: { arg: Arg | undefined; next: string; grant?: Grant },
) => arg !== undefined && submit(arg, next, grant)
) => (arg === undefined ? undefined : send(flow, arg, next, grant))

function submit(flows: Flows, action: ComplianceAction) {
function dispatch(flows: Flows, action: ComplianceAction, send: Send) {
switch (action.id) {
case "allow-address":
case "disallow-address":
return submitArg(flows[action.id], action)
return sendArg(send, flows[action.id], action)
case "freeze-address":
case "unfreeze-address":
return submitArg(flows[action.id], action)
return sendArg(send, flows[action.id], action)
case "freeze-shares":
case "unfreeze-shares":
return submitArg(flows[action.id], action)
return sendArg(send, flows[action.id], action)
case "force-transfer":
return submitArg(flows[action.id], action)
return sendArg(send, flows[action.id], action)
}
}

Expand Down Expand Up @@ -151,7 +156,12 @@ const ComplianceSection: React.FC<{
actions={actions}
pending={false}
unreadable={false}
onRun={(action) => submit(flows, action)}
onRun={(action) => dispatch(flows, action, submitTo)}
onReady={
active
? (action) => dispatch(flows, action, prepareFor)
: undefined
}
onInput={onInput}
/>
}
Expand Down
49 changes: 47 additions & 2 deletions app/src/components/admin/ConfigurationSection.test.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { fireEvent, render, screen, within } from "@testing-library/react"
import { beforeEach, describe, expect, it, vi } from "vitest"
import { act, fireEvent, render, screen, within } from "@testing-library/react"
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"
import { type Grant } from "../../hooks/useAdminAuthority"
import { type ConfigurationState } from "../../hooks/useConfigurationState"
import { type TransactionStatus } from "../../hooks/useContractTransaction"
Expand All @@ -18,6 +18,7 @@ const {
const tx = () => ({
status: { status: "idle" } as { status: string },
submit: vi.fn(() => true),
prepare: vi.fn(),
reset: vi.fn(),
})
return {
Expand Down Expand Up @@ -93,6 +94,10 @@ const submit = (name: string) =>
.find((candidate) => !candidate.hasAttribute("aria-expanded"))!

describe("ConfigurationSection", () => {
afterEach(() => {
vi.useRealTimers()
})

beforeEach(() => {
vi.clearAllMocks()
for (const mock of [
Expand Down Expand Up @@ -125,6 +130,46 @@ describe("ConfigurationSection", () => {
expect(within(token).getByText("Identity verifier")).toBeTruthy()
})

it("prepares a typed notice once it settles and still submits it on click", () => {
vi.useFakeTimers()
render(<ConfigurationSection grants={[governance]} wallet={WALLET} />)
expand("Set the notice")

fireEvent.change(screen.getByRole("textbox", { name: "Notice" }), {
target: { value: "2d" },
})
act(() => {
vi.advanceTimersByTime(400)
})

expect(noticeMock.prepare).toHaveBeenCalledWith(2n * DAY, governance)

fireEvent.click(submit("Set the notice"))

expect(noticeMock.submit).toHaveBeenCalledWith(2n * DAY, governance)
})

it("prepares nothing while the configuration tab is hidden", () => {
vi.useFakeTimers()
render(
<ConfigurationSection
grants={[governance]}
wallet={WALLET}
active={false}
/>,
)
expand("Set the notice")
fireEvent.change(screen.getByRole("textbox", { name: "Notice" }), {
target: { value: "2d" },
})

act(() => {
vi.advanceTimersByTime(400)
})

expect(noticeMock.prepare).not.toHaveBeenCalled()
})

it("submits a new notice once typed, and follows it in the modal", () => {
const { rerender } = render(
<ConfigurationSection grants={[governance]} wallet={WALLET} />,
Expand Down
24 changes: 15 additions & 9 deletions app/src/components/admin/ConfigurationSection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ import {
useSetWindDownDelay,
} from "../../hooks/useConfigurationTransactions"
import {
prepareFor,
type Send,
submitTo,
usePendingTransaction,
type UsePendingTransaction,
} from "../../hooks/usePendingTransaction"
Expand Down Expand Up @@ -83,23 +86,23 @@ const ConfigurationSection: React.FC<{

const actions = toConfigurationActions(configuration, grants, wallet, inputs)

const run = (action: ConfigurationAction) => {
const dispatch = (action: ConfigurationAction, send: Send) => {
if (action.arg === undefined) return
switch (action.id) {
case "set-deposit-cap":
return depositCap.submit(action.arg, action.next, action.grant)
return send(depositCap, action.arg, action.next, action.grant)
case "set-custodian":
return custodian.submit(action.arg, action.next, action.grant)
return send(custodian, action.arg, action.next, action.grant)
case "set-notice":
return notice.submit(action.arg, action.next, action.grant)
return send(notice, action.arg, action.next, action.grant)
case "set-wind-down-delay":
return windDownDelay.submit(action.arg, action.next, action.grant)
return send(windDownDelay, action.arg, action.next, action.grant)
case "set-oracle-config":
return oracleConfig.submit(action.arg, action.next, action.grant)
return send(oracleConfig, action.arg, action.next, action.grant)
case "set-compliance":
return compliance.submit(action.arg, action.next, action.grant)
return send(compliance, action.arg, action.next, action.grant)
case "set-identity-verifier":
return identityVerifier.submit(action.arg, action.next, action.grant)
return send(identityVerifier, action.arg, action.next, action.grant)
}
}

Expand All @@ -119,7 +122,10 @@ const ConfigurationSection: React.FC<{
actions={actions}
pending={configuration.status === "checking"}
unreadable={false}
onRun={run}
onRun={(action) => dispatch(action, submitTo)}
onReady={
active ? (action) => dispatch(action, prepareFor) : undefined
}
onInput={onInput}
/>
}
Expand Down
Loading
Loading