Skip to content

Commit 043081d

Browse files
committed
refactor: update documentation to match the newly refactored application spy
1 parent f843c6e commit 043081d

File tree

7 files changed

+101
-91
lines changed

7 files changed

+101
-91
lines changed

docs/code/index/classes/ApplicationSpy.md

Lines changed: 36 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6,25 +6,25 @@
66

77
# Class: ApplicationSpy\<TContract\>
88

9-
Defined in: [src/application-spy.ts:33](https://github.com/algorandfoundation/algorand-typescript-testing/blob/main/src/application-spy.ts#L33)
9+
Defined in: [src/application-spy.ts:32](https://github.com/algorandfoundation/algorand-typescript-testing/blob/main/src/application-spy.ts#L32)
1010

1111
## Type Parameters
1212

1313
### TContract
1414

15-
`TContract` *extends* `Contract`
15+
`TContract` *extends* `Contract` = `Contract`
1616

1717
## Constructors
1818

1919
### Constructor
2020

21-
> **new ApplicationSpy**\<`TContract`\>(`contract`): `ApplicationSpy`\<`TContract`\>
21+
> **new ApplicationSpy**\<`TContract`\>(`contract`?): `ApplicationSpy`\<`TContract`\>
2222
23-
Defined in: [src/application-spy.ts:45](https://github.com/algorandfoundation/algorand-typescript-testing/blob/main/src/application-spy.ts#L45)
23+
Defined in: [src/application-spy.ts:44](https://github.com/algorandfoundation/algorand-typescript-testing/blob/main/src/application-spy.ts#L44)
2424

2525
#### Parameters
2626

27-
##### contract
27+
##### contract?
2828

2929
`TContract` | `ConstructorFor`\<`TContract`\>
3030

@@ -34,19 +34,19 @@ Defined in: [src/application-spy.ts:45](https://github.com/algorandfoundation/al
3434

3535
## Properties
3636

37-
### contract
37+
### contract?
3838

39-
> **contract**: `TContract` \| `ConstructorFor`\<`TContract`\>
39+
> `optional` **contract**: `TContract` \| `ConstructorFor`\<`TContract`\>
4040
41-
Defined in: [src/application-spy.ts:43](https://github.com/algorandfoundation/algorand-typescript-testing/blob/main/src/application-spy.ts#L43)
41+
Defined in: [src/application-spy.ts:42](https://github.com/algorandfoundation/algorand-typescript-testing/blob/main/src/application-spy.ts#L42)
4242

4343
***
4444

4545
### on
4646

4747
> `readonly` **on**: `_TypedApplicationSpyCallBacks`\<`TContract`\>
4848
49-
Defined in: [src/application-spy.ts:40](https://github.com/algorandfoundation/algorand-typescript-testing/blob/main/src/application-spy.ts#L40)
49+
Defined in: [src/application-spy.ts:39](https://github.com/algorandfoundation/algorand-typescript-testing/blob/main/src/application-spy.ts#L39)
5050

5151
The `on` property is a proxy that allows you to register callbacks for specific method signatures.
5252
It dynamically creates methods based on the contract's methods.
@@ -57,7 +57,7 @@ It dynamically creates methods based on the contract's methods.
5757

5858
> **notify**(`itxn`): `void`
5959
60-
Defined in: [src/application-spy.ts:51](https://github.com/algorandfoundation/algorand-typescript-testing/blob/main/src/application-spy.ts#L51)
60+
Defined in: [src/application-spy.ts:50](https://github.com/algorandfoundation/algorand-typescript-testing/blob/main/src/application-spy.ts#L50)
6161

6262
#### Parameters
6363

@@ -71,19 +71,43 @@ Defined in: [src/application-spy.ts:51](https://github.com/algorandfoundation/al
7171

7272
***
7373

74+
### onAbiCall()
75+
76+
> **onAbiCall**(`methodSignature`, `callback`): `void`
77+
78+
Defined in: [src/application-spy.ts:69](https://github.com/algorandfoundation/algorand-typescript-testing/blob/main/src/application-spy.ts#L69)
79+
80+
Registers a callback for a specific method signature.
81+
82+
#### Parameters
83+
84+
##### methodSignature
85+
86+
`bytes`
87+
88+
##### callback
89+
90+
`AppSpyCb`
91+
92+
#### Returns
93+
94+
`void`
95+
96+
***
97+
7498
### onBareCall()
7599

76100
> **onBareCall**(`callback`): `void`
77101
78-
Defined in: [src/application-spy.ts:61](https://github.com/algorandfoundation/algorand-typescript-testing/blob/main/src/application-spy.ts#L61)
102+
Defined in: [src/application-spy.ts:60](https://github.com/algorandfoundation/algorand-typescript-testing/blob/main/src/application-spy.ts#L60)
79103

80104
Registers a callback for a bare call (no arguments).
81105

82106
#### Parameters
83107

84108
##### callback
85109

86-
`AppSpyCb`\<\[\], `unknown`\>
110+
`AppSpyCb`
87111

88112
The callback to be executed when a bare call is detected.
89113

docs/code/subcontexts/contract-context/classes/ContractContext.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
# Class: ContractContext
88

9-
Defined in: [src/subcontexts/contract-context.ts:145](https://github.com/algorandfoundation/algorand-typescript-testing/blob/main/src/subcontexts/contract-context.ts#L145)
9+
Defined in: [src/subcontexts/contract-context.ts:146](https://github.com/algorandfoundation/algorand-typescript-testing/blob/main/src/subcontexts/contract-context.ts#L146)
1010

1111
Provides a context for creating contracts and registering created contract instances
1212
with test execution context.
@@ -27,7 +27,7 @@ with test execution context.
2727

2828
> **create**\<`T`\>(`type`, ...`args`): `T`
2929
30-
Defined in: [src/subcontexts/contract-context.ts:157](https://github.com/algorandfoundation/algorand-typescript-testing/blob/main/src/subcontexts/contract-context.ts#L157)
30+
Defined in: [src/subcontexts/contract-context.ts:158](https://github.com/algorandfoundation/algorand-typescript-testing/blob/main/src/subcontexts/contract-context.ts#L158)
3131

3232
Creates a new contract instance and register the created instance with test execution context.
3333

@@ -72,7 +72,7 @@ const contract = ctx.contract.create(MyContract);
7272

7373
> `static` **createMethodCallTxns**\<`TParams`\>(`contract`, `abiMetadata`, ...`args`): `Transaction`[]
7474
75-
Defined in: [src/subcontexts/contract-context.ts:179](https://github.com/algorandfoundation/algorand-typescript-testing/blob/main/src/subcontexts/contract-context.ts#L179)
75+
Defined in: [src/subcontexts/contract-context.ts:180](https://github.com/algorandfoundation/algorand-typescript-testing/blob/main/src/subcontexts/contract-context.ts#L180)
7676

7777
**`Internal`**
7878

docs/testing-guide/application-spy.md

Lines changed: 38 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,8 @@ const spy = new ApplicationSpy(Hello)
4646

4747
// register a callback for the method of the contract.
4848
// the callback is registered against the method selector.
49-
// callbacks for multiple methods with same method selector are kept in an array
50-
// and all callbacks are invoked when any one of those methods is called.
49+
// all callbacks for multiple methods with same method selector are invoked
50+
// when any one of those methods is called.
5151
// in those cases, you can check for `itxnContext.approvalProgram` or `itxnContext.appId`
5252
// to see if the callback needs to handle a particular method call.
5353
// e.g.
@@ -57,10 +57,9 @@ const spy = new ApplicationSpy(Hello)
5757
// }
5858
// ```
5959
// `itxnContext` is provided as a parameter to the callback method and
60-
// it allows reading and setting of all properties of `itxn.ApplicationCallFields` interface
61-
// which are used to construct `itxn.ApplicationCall` transaction when `.submit()` is called.
62-
// it also maps `appArgs` to `args` property and provides consistent access to parameters passed
63-
// to the contract method.
60+
// it allows reading and setting of the properties of `itxn.ApplicationCallInnerTxn` interface.
61+
// it also maps and encodes the arugments to `appArgs` collection as bytes values,
62+
// and provides consistent access those arguments.
6463
spy.on.create((itxnContext) => {
6564
itxnContext.createdApp = helloApp
6665
})
@@ -98,7 +97,7 @@ ctx.setCompiledApp(Hello, helloApp.id)
9897
const spy = new ApplicationSpy(Hello)
9998

10099
// the mock setup is the same as using explicit create method except
101-
// the literal string keyword 'bareCreate' is used instead of a method signature
100+
// `onBareCall` method is used instead of `on.{methodName}` or `onAbiCall` methods
102101
// to register the callback
103102
spy.onBareCall((itxnContext) => {
104103
itxnContext.createdApp = helloApp
@@ -119,51 +118,54 @@ const txn = itxn
119118
const result = decodeArc4<string>(txn.lastLog, 'log')
120119
```
121120

122-
Mock result can be setup for the snippet above as
121+
**2. Strongly typed contract method call**
122+
123+
```ts
124+
const result = compiled.call.greet({
125+
args: ['world'],
126+
appId: app,
127+
}).returnValue
128+
assert(result === 'hello world')
129+
```
130+
131+
Mock result can be setup for both snippets above as
123132

124133
````ts
125-
// `itxnContext.returnValue` is added as the last entry to the logs of the constructed `itxn.ApplicationCall`
134+
// `itxnContext.setReturnValue` is added as the last entry to the logs of the constructed `itxn.ApplicationCall`
126135
// so that it can be access via `txn.lastLog` property.
127-
// it is available as `.retrunValue` when using strongly typed method call approach.
128-
// `returnValue` should only be set as the last statement of the callback and
136+
// `setReturnValue` should only be called as the last statement of the callback and
129137
// especially no further manipulations of logs should take place afterwards.
130-
// `appArgs` without the first value (which is the method selector) is available as `itxnContext.args`.
131-
// since it is encoded as `bytes`, it needs to be decoded to get back the string value.
138+
// `appArgs` collection holds method selector and method arguments encoded as `bytes` values.
139+
// They need to be decoded if the orginal argument values are needed.
132140
// you can check `itxnContext.appId` if there are multiple callback registered for the same method selector
133141
// e.g.
134142
// ```
135143
// if (itxnContext.appId === helloApp) {
136-
// itxnContext.returnValue = `hello ${decodeArc4<Str>(itxnContext.args[0])}`
144+
// itxnContext.returnValue = `hello ${decodeArc4<string>(itxnContext.appArgs(0))}`
137145
// }
138146
// ```
139147
spy.on.greet((itxnContext) => {
140-
itxnContext.returnValue = `hello ${decodeArc4<Str>(itxnContext.args[0])}`
148+
itxnContext.returnValue = `hello ${decodeArc4<string>(itxnContext.appArgs(0))}`
141149
})
142150
````
143151

144-
**2. Strongly typed contract method call**
145-
146-
```ts
147-
const result = compiled.call.greet({
148-
args: ['world'],
149-
appId: app,
150-
}).returnValue
151-
assert(result === 'hello world')
152-
```
153-
154-
Mock result can be setup for the snippet above as
152+
You can also use the alternative approach below to setup the mock result. It is especially useful if you do not have `Contract` subclass available and only method signature and application id are availbe to make the method call.
155153

156154
```ts
157-
// the setup is the same as in `itxn.applicationCall`, except the `itxnContext.args` contains
158-
// the values passed in `args` array in their original format with being encoded into bytes.
159-
spy.on.greet((itxnContext) => {
160-
itxnContext.returnValue = `hello ${itxnContext.args[0]}`
155+
// create a spy without the contract type provided
156+
const spy = new ApplicationSpy()
157+
158+
spy.onAbiCall(methodSelector('greet(string)string'), (itxnContext) => {
159+
// check for a well-known appId or the appId provided to the contract under test in some other manner
160+
if (itxnContext.appId === appId) {
161+
itxnContext.setReturnValue(`hey ${decodeArc4<string>(itxnContext.appArgs(1))}`)
162+
}
161163
})
162164
```
163165

164166
**3. Strongly typed ABI calls**
165167

166-
```
168+
```ts
167169
const result = abiCall(Hello.prototype.greet, {
168170
appId: app,
169171
args: ['abi'],
@@ -175,41 +177,10 @@ Mock result can be setup for the snippet above as
175177
```ts
176178
// the setup is the same as the previous case
177179
spy.on.greet((itxnContext) => {
178-
itxnContext.returnValue = `hello ${itxnContext.args[0]}`
180+
itxnContext.setReturnValue(`hello ${decodeArc4<string>(itxnContext.appArgs(0))}`)
179181
})
180182
```
181183

182-
### Key Features
183-
184-
1. **Type-safe Method Mocking**
185-
186-
```ts
187-
spy.on.increment((itxnContext) => {
188-
// itxnContext.args is properly typed based on method signature
189-
itxnContext.returnValue = 1n // Type checked against method return type
190-
})
191-
```
192-
193-
2. **Creation Handling**
194-
195-
```ts
196-
spy.on.create((itxnContext) => {
197-
// Handle contract creation
198-
itxnContext.createdApp = counterApp
199-
})
200-
```
201-
202-
3. **Multiple Contract Support**
203-
204-
```ts
205-
// Create spies for different contracts
206-
const counterSpy = new ApplicationSpy(Counter)
207-
const vaultSpy = new ApplicationSpy(Vault)
208-
209-
ctx.addApplicationSpy(counterSpy)
210-
ctx.addApplicationSpy(vaultSpy)
211-
```
212-
213184
### Best Practices
214185

215186
1. **Reset Between Tests**
@@ -226,15 +197,16 @@ spy.on.greet((itxnContext) => {
226197
spy.on.increment((itxnContext) => {
227198
// Only handle calls to specific app instance
228199
if (itxnContext.appId === counterApp) {
229-
itxnContext.returnValue = 1n
200+
itxnContext.setReturnValue(1n)
230201
}
231202
})
232203
```
233204

234205
3. **Handle Method Arguments**
235206
```ts
236207
spy.on.setValue((itxnContext) => {
237-
//`itxnContext.args` could be encoded as bytes if `itxn.applicationCall` is used to make the call
238-
itxnContext.returnValue = `hello ${decodeArc4<Str>(itxnContext.args[0])}`
208+
// arguments provided to the method are encoded as bytes values
209+
// and available via `itxnContext.appArgs` method
210+
itxnContext.setReturnValue(`hello ${decodeArc4<string>(itxnContext.appArgs(0))}`)
239211
})
240212
```

docs/testing-guide/transactions.md

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -189,7 +189,6 @@ If your contract needs to deploy other contracts then it's likely you will need
189189
```ts
190190
import { assert, compile, Contract, GlobalState, itxn, OnCompleteAction } from '@algorandfoundation/algorand-typescript'
191191
import { ApplicationSpy, TestExecutionContext } from '@algorandfoundation/algorand-typescript-testing'
192-
import type { Str } from '@algorandfoundation/algorand-typescript/arc4'
193192
import { abimethod, decodeArc4, encodeArc4, methodSelector } from '@algorandfoundation/algorand-typescript/arc4'
194193
import { afterEach, describe, it } from 'vitest'
195194

@@ -257,7 +256,7 @@ describe('pre compiled app calls', () => {
257256
itxnContext.createdApp = helloApp
258257
})
259258
spy.on.greet((itxnContext) => {
260-
itxnContext.returnValue = `hello ${decodeArc4<Str>(itxnContext.args[0])}`
259+
itxnContext.setReturnValue(`hello ${decodeArc4<string>(itxnContext.appArgs(1))}`)
261260
})
262261
ctx.addApplicationSpy(spy)
263262

@@ -274,9 +273,9 @@ describe('pre compiled app calls', () => {
274273
Assuming the contract you wish to compile extends the ARC4 `Contract` type, you can make use of `compileArc4` to produce a contract proxy object that makes it easy to invoke application methods with compile time type safety. You can use the same `ctx.setCompiledApp` method set up the mock result for `compile` call and `ApplicationSpy` for mocking subsequent calls to the compiled contract.
275274

276275
```ts
277-
import { assert, Contract, GlobalState, OnCompleteAction } from '@algorandfoundation/algorand-typescript'
276+
import { assert, Contract, GlobalState } from '@algorandfoundation/algorand-typescript'
278277
import { ApplicationSpy, TestExecutionContext } from '@algorandfoundation/algorand-typescript-testing'
279-
import { abimethod, compileArc4 } from '@algorandfoundation/algorand-typescript/arc4'
278+
import { abimethod, compileArc4, decodeArc4 } from '@algorandfoundation/algorand-typescript/arc4'
280279
import { afterEach, describe, it } from 'vitest'
281280

282281
export class Hello extends Contract {
@@ -332,10 +331,7 @@ describe('pre compiled typed app calls', () => {
332331
itxnContext.createdApp = helloApp
333332
})
334333
spy.on.greet((itxnContext) => {
335-
itxnContext.returnValue = `hello ${itxnContext.args[0]}`
336-
})
337-
spy.on.delete((itxnContext) => {
338-
itxnContext.onCompletion = OnCompleteAction.DeleteApplication
334+
itxnContext.setReturnValue(`hello ${decodeArc4<string>(itxnContext.appArgs(1))}`)
339335
})
340336
ctx.addApplicationSpy(spy)
341337

@@ -355,7 +351,7 @@ If your use case does not require deploying another contract, and instead you ar
355351
import type { Application } from '@algorandfoundation/algorand-typescript'
356352
import { assert, Contract, GlobalState } from '@algorandfoundation/algorand-typescript'
357353
import { ApplicationSpy, TestExecutionContext } from '@algorandfoundation/algorand-typescript-testing'
358-
import { abiCall, abimethod } from '@algorandfoundation/algorand-typescript/arc4'
354+
import { abiCall, abimethod, decodeArc4 } from '@algorandfoundation/algorand-typescript/arc4'
359355
import { afterEach, describe, it } from 'vitest'
360356

361357
export class Hello extends Contract {
@@ -398,7 +394,7 @@ describe('pre compiled typed app calls', () => {
398394

399395
const spy = new ApplicationSpy(Hello)
400396
spy.on.greet((itxnContext) => {
401-
itxnContext.returnValue = `hello ${itxnContext.args[0]}`
397+
itxnContext.setReturnValue(`hello ${decodeArc4<string>(itxnContext.appArgs(1))}`)
402398
})
403399
ctx.addApplicationSpy(spy)
404400

src/application-spy.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,11 @@ export class ApplicationSpy<TContract extends Contract = Contract> {
6161
this.#spyFns.push(predicates.bareCall(callback))
6262
}
6363

64+
/**
65+
* Registers a callback for a specific method signature.
66+
* @param methodSignature
67+
* @param callback
68+
*/
6469
onAbiCall(methodSignature: bytes, callback: AppSpyCb) {
6570
this.#spyFns.push(predicates.methodSelector(callback, methodSignature))
6671
}

0 commit comments

Comments
 (0)