Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Wasm examples to CI #345

Merged
merged 13 commits into from Aug 5, 2021
2 changes: 1 addition & 1 deletion .github/workflows/audit.yml
Expand Up @@ -2,7 +2,7 @@ name: Audit

on:
schedule:
- cron: '0 0 * * *'
- cron: '0 0 * * *' # run at midnight every day
push:
branches:
- main
Expand Down
4 changes: 4 additions & 0 deletions .github/workflows/build-and-test.yml
Expand Up @@ -359,6 +359,10 @@ jobs:
command: test
args: --manifest-path ./bindings/wasm/Cargo.toml --release

- name: Run wasm-bindgen tests # tests annotated with #[wasm_bindgen_test]
run: wasm-pack test --node
working-directory: bindings/wasm

- name: Print sccache stats
run: sccache --show-stats

Expand Down
56 changes: 56 additions & 0 deletions .github/workflows/examples.yml
@@ -0,0 +1,56 @@
name: Wasm Examples

on:
schedule:
- cron: '0 0 * * *' # run at midnight every day
workflow_dispatch:
inputs:
ref:
description: "Branch/ref (default dev branch)"
required: true
default: 'dev' # checkout dev branch by default, not main

jobs:
build-and-test-examples:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
with:
ref: ${{ github.event.inputs.ref }}

- name: Install stable toolchain
uses: actions-rs/toolchain@v1
with:
toolchain: stable
override: true

- name: Set up Node.js
uses: actions/setup-node@v1
with:
node-version: 15.x

- name: Install txm # for running README.md examples https://github.com/anko/txm
run: yarn global add txm

- name: Install wasm-pack
run: yarn global add wasm-pack

- name: Install JS dependencies
run: yarn
working-directory: bindings/wasm

- name: Build Wasm bindings
run: yarn build
working-directory: bindings/wasm

- name: Test Wasm examples (node)
run: yarn run test:node
working-directory: bindings/wasm

- name: Test Wasm examples (browser)
run: yarn run test:browser
working-directory: bindings/wasm

- name: Test Wasm README.md examples
run: yarn run test:readme
working-directory: bindings/wasm
22 changes: 15 additions & 7 deletions bindings/wasm/.gitignore
@@ -1,11 +1,19 @@
/target
# Rust
target/
Cargo.lock

node_modules
wasm-web
wasm-node
web
node
pkg
# npm / yarn
node_modules/

# Build artifacts
wasm-web/
wasm-node/
web/
node/
pkg/

# cypress
cypress/screenshots/
cypress/videos/

.DS_STORE
44 changes: 31 additions & 13 deletions bindings/wasm/README.md
Expand Up @@ -24,7 +24,7 @@ $ yarn add @iota/identity-wasm@dev

Alternatively, you can build the bindings if you have Rust installed. If not, refer to [rustup.rs](https://rustup.rs) for the installation. Then install the necessary dependencies using:

```npm install```
```yarn``` or ```npm install```

and then build the bindings for `node.js` with

Expand All @@ -34,28 +34,46 @@ or for the `web` with

```npm run build:web```

## NodeJS Setup

```js
## NodeJS Usage
<!--
Test this example using https://github.com/anko/txm: `txm README.md`

Replace imports with local paths for txm:
!test program
cat \
| sed -e "s#require('@iota/identity-wasm/node')#require('./node/identity_wasm.js')#" \
| node
-->
<!-- !test check Nodejs Example -->
```javascript
const identity = require('@iota/identity-wasm/node')

// Generate a new KeyPair
const key = new identity.KeyPair(identity.KeyType.Ed25519)

// Create a new DID Document with the KeyPair as the default authentication method
const doc = identity.Document.fromKeyPair(key)
// const doc = identity.Document.fromKeyPair(key, "test") // if using the testnet

// Sign the DID Document with the sceret key
// Sign the DID Document with the private key
doc.sign(key)

// Create a default client instance for the mainnet
const config = identity.Config.fromNetwork(identity.Network.mainnet())
// const config = identity.Config.fromNetwork(identity.Network.testnet()); // if using the testnet
const client = identity.Client.fromConfig(config)

// Publish the DID Document to the IOTA Tangle
identity.publish(doc.toJSON(), { node: "https://nodes.thetangle.org:443" })
.then((message) => {
console.log("Tangle Message Id: ", message)
console.log("Tangle Message Url", `https://explorer.iota.org/mainnet/transaction/${message}`)
}).catch((error) => {
console.error("Error: ", error)
})
// The message can be viewed at https://explorer.iota.org/<mainnet|testnet>/transaction/<messageId>
client.publishDocument(doc.toJSON())
.then((receipt) => {
console.log("Tangle Message Receipt: ", receipt)
console.log("Tangle Message Url:", doc.id.network.messageURL(receipt.messageId))
})
.catch((error) => {
console.error("Error: ", error)
throw error
})
```

## Web Setup
Expand Down Expand Up @@ -114,7 +132,7 @@ new CopyWebPlugin({
}),
```

### Usage
### Web Usage

```js
import * as identity from "@iota/identity-wasm/web";
Expand Down
4 changes: 4 additions & 0 deletions bindings/wasm/cypress.json
@@ -0,0 +1,4 @@
{
"screenshotOnRunFailure": false,
"video": false
}
Empty file.
85 changes: 85 additions & 0 deletions bindings/wasm/cypress/integration/browser_test.js
@@ -0,0 +1,85 @@
import { defaultClientConfig, initIdentity } from "../../examples/browser/utils";
import { createIdentity } from "../../examples/browser/create_did.js";
import { createVC } from "../../examples/browser/create_vc.js";
import { manipulateIdentity } from "../../examples/browser/mainpulate_did.js";
import { resolveIdentity } from "../../examples/browser/resolve.js";
import { createVP } from "../../examples/browser/create_vp.js";
import { revoke } from "../../examples/browser/revocation.js";
import { merkleKey } from "../../examples/browser/merkle_key.js";

// Test that the browser examples do not throw uncaught exceptions twice, including syntax errors etc.
describe(
"Test browser examples",
{
defaultCommandTimeout: 120000, // 2 minutes to account for spurious network delays
},
() => {
beforeEach(async () => {
// The working directory is under __cypress at test runtime, so we need to go up one more level than usual
await initIdentity("../../../web/identity_wasm_bg.wasm", false);

// NOTE: `cy.wrap(defaultClientConfig()).as('config')` does not always work to make the config available
// from the shared context as `this.config` because it has a race condition with initializing the wasm.
// So call `defaultClientConfig()` manually for now.
});

it("create identity", async function () {
let identityResult;
try {
identityResult = await createIdentity(defaultClientConfig(), false);
} catch (e) {
identityResult = await createIdentity(defaultClientConfig(), false);
}
// example of testing the output, can remove if needed
expect(identityResult).to.have.all.keys("key", "doc", "receipt", "explorerUrl");
});

it("manipulate identity", async function () {
try {
await manipulateIdentity(defaultClientConfig(), false);
} catch (e) {
await manipulateIdentity(defaultClientConfig(), false);
}
});

it("resolve identity", async function () {
try {
await resolveIdentity(defaultClientConfig(), false, false);
} catch (e) {
await resolveIdentity(defaultClientConfig(), false, false);
}
});

it("create verifiable credential", async function () {
try {
await createVC(defaultClientConfig(), false);
} catch (e) {
await createVC(defaultClientConfig(), false);
}
});

it("revoke verifiable credential", async function () {
try {
await revoke(defaultClientConfig(), false);
} catch (e) {
await revoke(defaultClientConfig(), false);
}
});

it("create verifiable presentation", async function () {
try {
await createVP(defaultClientConfig(), false);
} catch (e) {
await createVP(defaultClientConfig(), false);
}
});

it("merkle key", async function () {
try {
await merkleKey(defaultClientConfig(), false);
} catch (e) {
await merkleKey(defaultClientConfig(), false);
}
});
}
);
Empty file.
Empty file.
6 changes: 3 additions & 3 deletions bindings/wasm/examples/browser/create_did.js
Expand Up @@ -6,15 +6,15 @@ import {
getExplorerUrl,
} from "./utils.js";

/*
/**
This example shows a basic introduction on how to create a basic DID Document and upload it to the Tangle.
A ED25519 Keypair is generated, from which the public key is hashed, becoming the DID.
The keypair becomes part of the DID Document in order to prove a link between the DID and the published DID Document.
That same keypair should be used to sign the original DID Document.

@param {{network: string, node: string}} clientConfig
@param {{defaultNodeURL: string, explorerURL: string, network: Network}} clientConfig
@param {boolean} log log the events to the output window
*/
**/
export async function createIdentity(clientConfig, log = true) {
if (log) logToScreen("Identity creation started...");
if (log)
Expand Down
9 changes: 5 additions & 4 deletions bindings/wasm/examples/browser/create_vc.js
Expand Up @@ -2,15 +2,16 @@ import * as identity from "../../web/identity_wasm.js";
import { manipulateIdentity } from "./mainpulate_did.js";
import { createIdentity } from "./create_did.js";
import { logObjectToScreen, logToScreen } from "./utils.js";
/*

/**
This example shows how to create a Verifiable Credential and validate it.
In this example, alice takes the role of the subject, while we also have an issuer.
The issuer signs a UniversityDegreeCredential type verifiable credential with Alice's name and DID.
This Verifiable Credential can be verified by anyone, allowing Alice to take control of it and share it with whoever they please.
@param {{network: string, node: string}} clientConfig
@param {boolean} log log the events to the output window

*/
@param {{defaultNodeURL: string, explorerURL: string, network: Network}} clientConfig
@param {boolean} log log the events to the output window
**/
export async function createVC(clientConfig, log = true) {
if (log) logToScreen("Verifiable Credential creation started...");

Expand Down
6 changes: 3 additions & 3 deletions bindings/wasm/examples/browser/create_vp.js
Expand Up @@ -2,14 +2,14 @@ import * as identity from "../../web/identity_wasm.js";
import { createVC } from "./create_vc.js";
import { logObjectToScreen, logToScreen } from "./utils.js";

/*
/**
This example shows how to create a Verifiable Presentation and validate it.
A Verifiable Presentation is the format in which a (collection of) Verifiable Credential(s) gets shared.
It is signed by the subject, to prove control over the Verifiable Credential with a nonce or timestamp.

@param {{network: string, node: string}} clientConfig
@param {{defaultNodeURL: string, explorerURL: string, network: Network}} clientConfig
@param {boolean} log log the events to the output window
*/
**/
export async function createVP(clientConfig, log = true) {
if (log) logToScreen("creating Verifiable Presentation...");

Expand Down