Skip to content

Commit

Permalink
feat: clarinet-sdk: vitest config and custom matchers (#1151)
Browse files Browse the repository at this point in the history
* feat: vitest config and helpers for clarinet projects

* feat: implement clarity value formatter

* feat: improve and actually use cv formatter

* chore: preparing for the release

* feat: improve sdk tx handling for transfer stx and deploy contract

* refactor: remove dead code

* chore: upgrade dependencies

* fix: parse json clarity args

* refactor: self review

* refactor: improve types

* fix: remove pub function

* chore: prep release

* docs: typo

Co-authored-by: Micaiah Reid <[email protected]>

* docs: typo

Co-authored-by: Micaiah Reid <[email protected]>

* refactor: type

* chore: upgrade dependencies

* docs: update readme

* chore: restore deleted deployment plans

* docs: update sdk readme

* fix: use the right packaged name

* docs: update components/clarinet-sdk/README.md

---------

Co-authored-by: Micaiah Reid <[email protected]>
  • Loading branch information
hugocaillard and MicaiahReid authored Sep 21, 2023
1 parent edf98aa commit 0e4ec1e
Show file tree
Hide file tree
Showing 26 changed files with 3,559 additions and 405 deletions.
39 changes: 32 additions & 7 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,14 @@ plan:
path: contracts/counter.clar
anchor-block-only: true
clarity-version: 1
epoch: "2.0"
- id: 1
transactions:
- contract-publish:
contract-name: counter-2
expected-sender: ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM
cost: 6940
path: contracts/counter-v2.clar
anchor-block-only: true
clarity-version: 2
epoch: "2.4"
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,4 @@ plan:
method: increment
parameters:
- u1
cost: 5960
cost: 5960
8 changes: 4 additions & 4 deletions components/clarinet-sdk/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,15 @@ name = "clarinet_sdk"
path = "src/lib.rs"

[dependencies]
serde = { version = "1.0.136", features = ["derive"] }
serde_json = "1.0"
clarinet-files = { path = "../clarinet-files", default-features = false }
clarity-repl = { path = "../clarity-repl", default-features = false, optional = true }
clarinet-deployments = { path = "../clarinet-deployments", default-features = false }
# WASM
console_error_panic_hook = { version = "0.1", optional = true }
gloo-utils = { version = "0.2", features = ["serde"] }
js-sys = { version = "0.3", optional = true }
serde-wasm-bindgen = { version = "0.5", optional = true }
serde = { version = "1.0.136", features = ["derive"] }
serde_json = "1.0"
serde-wasm-bindgen = { version = "0.6.0", optional = true }
wasm-bindgen = { version = "0.2", optional = true }
wasm-bindgen-futures = { version = "0.4", optional = true }
web-sys = { version = "0.3", features = ["console"], optional = true }
Expand Down
177 changes: 169 additions & 8 deletions components/clarinet-sdk/README.md
Original file line number Diff line number Diff line change
@@ -1,20 +1,33 @@
# Clarinet SDK

The Clarinet SDK can be used to interact with the simnet from Node.js.

You can use this SDK to:
- call public and read-only functions from smart contracts
- get clarity maps or data-var values
- deploy contracts
- get contract ABI
- write unit tests for Clarity smart contracts

## Core

```
npm install @hirosystems/clarinet-sdk
```

### Usage

```ts
import { initVM } from "@hirosystems/clarinet-sdk";
import { Cl } from "@stacks/transactions";
import { initVM } from "obscurity-sdk";

async function main() {
const vm = await initVM();

const accounts = vm.getAccounts();
const w1 = accounts.get("wallet_1");
if (!w1) return;
const w1 = accounts.get("wallet_1")!;

const call = vm.callPublicFn("counter", "increment", [Cl.uint(1)], w1);
const call = vm.callPublicFn("counter", "add", [Cl.uint(1)], w1);
console.log(call.result); // Cl.int(Cl.ok(true))

const counter = vm.getDataVar("counter", "counter");
Expand All @@ -24,20 +37,168 @@ async function main() {
main();
```

## Contributing
By default, the SDK will look for a Clarinet.toml file in the current working directory.
It's also possible to provide the path to the manifest like so:
```ts
const vm = await initVM("./path/to/Clarinet.toml");
```

## Tests

> Note: A bit of boilerplate is needed to setup the testing environment. Soon it will be handled by the clarinet-cli.
The SDK can be used to write unit-tests for Clarinet projects.
Make sure you are in directory with a Clarinet.toml file and the associated Clarity smart contracts:

```sh
cd ./my-project
ls # here you should see the Clarinet.toml file
```

Clone the clarinet repo adn switch to the sdk component
Let's initialize the Node.js project:
```sh
npm init -y # the -y option sets default properties
npm install @hirosystems/clarinet-sdk @stacks/transactions vite vitest vitest-environment-clarinet
```

Update the package.json file scripts to handle tests:
```json
"scripts": {
"test": "vitest run",
"test:coverage": "vitest run -- --coverage true"
},
```

The `./.gitignore` file also needs to be updated, add the following lines at the end. It is especially important to ignore `node_modules`.
```
logs
*.log
npm-debug.log*
coverage
*.info
node_modules
```

A config file is needed for Vitest to use the clarinet-environment.
Create the file `vitest.config.js` with the following content:
```js
/// <reference types="vitest" />

import { defineConfig } from "vite";
import { vitestSetupFilePath, getClarinetVitestsArgv } from "@hirosystems/clarinet-sdk/vitest";

export default defineConfig({
test: {
environment: "clarinet",
singleThread: true,
setupFiles: [vitestSetupFilePath],
environmentOptions: {
clarinet: getClarinetVitestsArgv(),
},
},
});
```

The set up is ready, let's write the first test. Create a test file in the `unit-tests` directory:

```sh
mkdir unit-tests
touch unit-tests/my-contract.test.js
```

```js
// unit-tests/my-contract.test.js
import { describe, it, expect } from "vitest";
import { Cl } from "@stacks/transactions";

describe("test counter ONE", () => {
const accounts = vm.getAccounts();
const w1 = accounts.get("wallet_1");
if (!w1) throw new Error("wallet_1 does not exist");

it("adds two numbers", () => {
const callAdd = vm.callPublicFn("my-contract", "add", [Cl.uint(21), Cl.uint(21)], w1);
expect(callAdd.result).toBeOk(Cl.uint(42));
});
});

```

### Notes:

- This code assumes that you have a contract called `my-contract` with a method `add`.
```clar
;; contracts/my-contract.clar
(define-public (add (n1 uint) (n2 uint))
(ok (+ n1 n2))
)
```

- You may need to disable the deno extension if it's activated in `.vscode/settings.json`.


### Type checking

You can use TypeScript to write test by installing it and setting up the `tsconfig.json`.

```sh
npm install typescript
touch tsconfig.json
```

```json
{
"compilerOptions": {
"target": "ESNext",
"useDefineForClassFields": true,
"module": "ESNext",
"lib": ["ESNext"],
"skipLibCheck": true,

"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,

"strict": true,
"noImplicitAny": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true
},
"include": ["node_modules/@hirosystems/clarinet-sdk/vitest-helpers/src", "unit-tests"]
}

```

If you want to write your test in JavaScript but still have a certain level of type safety and autocompletion, VSCode can help you with that. You can create a basic `jsconfig.json` file:

```json
{
"compilerOptions": {
"checkJs": true,
"strict": true
},
"include": ["node_modules/@hirosystems/clarinet-sdk/vitest-helpers/src", "unit-tests"]
}
```

## Contributing

Clone the clarinet repo and go to the clarinet-sdk component directory:
```sh
git clone [email protected]:hirosystems/clarinet.git
cd clarinet/components/clarinet-sdk
```

Open the SDK workspace in VSCode:
```
```sh
code ./clarinet-sdk.code-workspace
```

Compile the project (both WASM and JS):
```
```sh
npm install
npm run build
```
2 changes: 0 additions & 2 deletions components/clarinet-sdk/clarinet-sdk.code-workspace
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
{
"folders": [{ "path": "../../" }],
"settings": {
// "rust-analyzer.checkOnSave.command": "clippy -p clarinet-sdk -- --no-deps",
// "rust-analyzer.check.command": "check",
"rust-analyzer.check.overrideCommand": [
"cargo",
"clippy",
Expand Down
Loading

0 comments on commit 0e4ec1e

Please sign in to comment.