Skip to content

Commit

Permalink
test: add browser test to canary flow (#1808)
Browse files Browse the repository at this point in the history
Adding Aztec.js browser test to our canary flow to ensure published npm
package is stable

# Checklist:
Remove the checklist to signal you've completed it. Enable auto-merge if
the PR is ready to merge.
- [ ] If the pull request requires a cryptography review (e.g.
cryptographic algorithm implementations) I have added the 'crypto' tag.
- [ ] I have reviewed my diff in github, line by line and removed
unexpected formatting changes, testing logs, or commented-out code.
- [ ] Every change is related to the PR description.
- [ ] I have
[linked](https://docs.github.com/en/issues/tracking-your-work-with-issues/linking-a-pull-request-to-an-issue)
this pull request to relevant issues (if any exist).
  • Loading branch information
spypsy authored Aug 25, 2023
1 parent b10656d commit 7f4fa43
Show file tree
Hide file tree
Showing 5 changed files with 312 additions and 3 deletions.
23 changes: 20 additions & 3 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1133,10 +1133,10 @@ jobs:
- *checkout
- *setup_env
- run:
name: "Build and test"
name: "Build"
command: build canary true

run-deployment-canary:
run-deployment-canary-uniswap:
docker:
- image: aztecprotocol/alpine-build-image
resource_class: small
Expand All @@ -1147,6 +1147,17 @@ jobs:
name: "Test"
command: spot_run_test_script ./scripts/run_tests canary uniswap_trade_on_l1_from_l2.test.ts canary docker-compose.yml

run-deployment-canary-browser:
docker:
- image: aztecprotocol/alpine-build-image
resource_class: small
steps:
- *checkout
- *setup_env
- run:
name: "Test"
command: spot_run_test_script ./scripts/run_tests canary aztec_js_browser.test.test.ts canary docker-compose.yml

# Repeatable config for defining the workflow below.
tag_regex: &tag_regex /^v.*/
defaults: &defaults
Expand Down Expand Up @@ -1396,7 +1407,13 @@ workflows:
- deploy-end
<<: *deploy_defaults

- run-deployment-canary:
- run-deployment-canary-uniswap:
requires:
- build-deployment-canary
<<: *deploy_defaults

- run-deployment-canary-browser:
requires:
- build-deployment-canary
<<: *deploy_defaults

1 change: 1 addition & 0 deletions yarn-project/aztec.js/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ export {
PackedArguments,
PublicKey,
PrivateKey,
SyncStatus,
Tx,
TxExecutionRequest,
TxHash,
Expand Down
4 changes: 4 additions & 0 deletions yarn-project/canary/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,12 @@
"@aztec/noir-contracts": "workspace:^",
"@jest/globals": "^29.5.0",
"@types/jest": "^29.5.0",
"@types/koa-static": "^4.0.2",
"@types/node": "^18.7.23",
"jest": "^29.5.0",
"koa": "^2.14.2",
"koa-static": "^5.0.0",
"puppeteer": "^21.1.0",
"ts-jest": "^29.1.0",
"ts-node": "^10.9.1",
"tslib": "^2.4.0",
Expand Down
216 changes: 216 additions & 0 deletions yarn-project/canary/src/aztec_js_browser.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,216 @@
/* eslint-disable no-console */
import * as AztecJs from '@aztec/aztec.js';
import { AztecAddress, PrivateKey } from '@aztec/circuits.js';
import { DebugLogger, createDebugLogger } from '@aztec/foundation/log';
import { PrivateTokenContractAbi } from '@aztec/noir-contracts/artifacts';

import { Server } from 'http';
import Koa from 'koa';
import serve from 'koa-static';
import path, { dirname } from 'path';
import { Browser, Page, launch } from 'puppeteer';
import { fileURLToPath } from 'url';

declare global {
interface Window {
AztecJs: typeof AztecJs;
}
}

const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);

const PORT = 3000;

const { SANDBOX_URL } = process.env;

const conditionalDescribe = () => (SANDBOX_URL ? describe : describe.skip);
const privKey = PrivateKey.random();

/**
* This test is a bit of a special case as it's relying on sandbox and web browser and not only on anvil and node.js.
* To run the test, do the following:
* 1) Build the whole repository,
* 2) go to `yarn-project/aztec.js` and build the web packed package with `yarn build:web`,
* 3) start anvil: `anvil`,
* 4) open new terminal and optionally set the more verbose debug level: `DEBUG=aztec:*`,
* 5) go to the sandbox dir `yarn-project/aztec-sandbox` and run `yarn start`,
* 6) open new terminal and export the sandbox URL: `export SANDBOX_URL='http://localhost:8080'`,
* 7) go to `yarn-project/end-to-end` and run the test: `yarn test aztec_js_browser`
*
* NOTE: If you see aztec-sandbox logs spammed with unexpected logs there is probably a chrome process with a webpage
* unexpectedly running in the background. Kill it with `killall chrome`
*/
conditionalDescribe()('e2e_aztec.js_browser', () => {
const initialBalance = 33n;
const transferAmount = 3n;

let contractAddress: AztecAddress;

let logger: DebugLogger;
let pageLogger: DebugLogger;
let app: Koa;
let testClient: AztecJs.AztecRPC;
let server: Server;

let browser: Browser;
let page: Page;

beforeAll(async () => {
testClient = AztecJs.createAztecRpcClient(SANDBOX_URL!, AztecJs.makeFetch([1, 2, 3], true));
await AztecJs.waitForSandbox(testClient);

app = new Koa();
app.use(serve(path.resolve(__dirname, './web')));
server = app.listen(PORT, () => {
logger(`Server started at http://localhost:${PORT}`);
});

logger = createDebugLogger('aztec:aztec.js:web');
pageLogger = createDebugLogger('aztec:aztec.js:web:page');

browser = await launch({
executablePath: process.env.CHROME_BIN,
headless: 'new',
args: [
'--allow-file-access-from-files',
'--no-sandbox',
'--headless',
'--disable-web-security',
'--disable-features=IsolateOrigins',
'--disable-site-isolation-trials',
'--disable-gpu',
'--disable-dev-shm-usage',
'--disk-cache-dir=/dev/null',
],
});
page = await browser.newPage();
page.on('console', msg => {
pageLogger(msg.text());
});
page.on('pageerror', err => {
pageLogger.error(err.toString());
});
await page.goto(`http://localhost:${PORT}/index.html`);
}, 120_000);

afterAll(async () => {
await browser.close();
server.close();
});

it('Loads Aztec.js in the browser', async () => {
const createAccountsExists = await page.evaluate(() => {
const { createAccounts } = window.AztecJs;
return typeof createAccounts === 'function';
});
expect(createAccountsExists).toBe(true);
});

it('Creates an account', async () => {
const result = await page.evaluate(
async (rpcUrl, privateKeyString) => {
const { PrivateKey, createAztecRpcClient, makeFetch, getUnsafeSchnorrAccount } = window.AztecJs;
const client = createAztecRpcClient(rpcUrl!, makeFetch([1, 2, 3], true));
const privateKey = PrivateKey.fromString(privateKeyString);
const account = getUnsafeSchnorrAccount(client, privateKey);
await account.waitDeploy();
const completeAddress = await account.getCompleteAddress();
const addressString = completeAddress.address.toString();
console.log(`Created Account: ${addressString}`);
return addressString;
},
SANDBOX_URL,
privKey.toString(),
);
const accounts = await testClient.getAccounts();
const stringAccounts = accounts.map(acc => acc.address.toString());
expect(stringAccounts.includes(result)).toBeTruthy();
}, 15_000);

it('Deploys Private Token contract', async () => {
await deployPrivateTokenContract();
}, 30_000);

it("Gets the owner's balance", async () => {
const result = await page.evaluate(
async (rpcUrl, contractAddress, PrivateTokenContractAbi) => {
const { Contract, AztecAddress, createAztecRpcClient, makeFetch } = window.AztecJs;
const client = createAztecRpcClient(rpcUrl!, makeFetch([1, 2, 3], true));
const owner = (await client.getAccounts())[0].address;
const wallet = await AztecJs.getSandboxAccountsWallet(client);
const contract = await Contract.at(AztecAddress.fromString(contractAddress), PrivateTokenContractAbi, wallet);
const balance = await contract.methods.getBalance(owner).view({ from: owner });
return balance;
},
SANDBOX_URL,
(await getPrivateTokenAddress()).toString(),
PrivateTokenContractAbi,
);
logger('Owner balance:', result);
expect(result).toEqual(initialBalance);
});

it('Sends a transfer TX', async () => {
const result = await page.evaluate(
async (rpcUrl, contractAddress, transferAmount, PrivateTokenContractAbi) => {
console.log(`Starting transfer tx`);
const { AztecAddress, Contract, createAztecRpcClient, makeFetch } = window.AztecJs;
const client = createAztecRpcClient(rpcUrl!, makeFetch([1, 2, 3], true));
const accounts = await client.getAccounts();
const owner = accounts[0].address;
const receiver = accounts[1].address;
const wallet = await AztecJs.getSandboxAccountsWallet(client);
const contract = await Contract.at(AztecAddress.fromString(contractAddress), PrivateTokenContractAbi, wallet);
await contract.methods.transfer(transferAmount, owner, receiver).send({ origin: owner }).wait();
console.log(`Transferred ${transferAmount} tokens to new Account`);
return await contract.methods.getBalance(receiver).view({ from: receiver });
},
SANDBOX_URL,
(await getPrivateTokenAddress()).toString(),
transferAmount,
PrivateTokenContractAbi,
);
expect(result).toEqual(transferAmount);
}, 60_000);

const deployPrivateTokenContract = async () => {
const txHash = await page.evaluate(
async (rpcUrl, privateKeyString, initialBalance, PrivateTokenContractAbi) => {
const { PrivateKey, DeployMethod, createAztecRpcClient, makeFetch, getUnsafeSchnorrAccount } = window.AztecJs;
const client = createAztecRpcClient(rpcUrl!, makeFetch([1, 2, 3], true));
let accounts = await client.getAccounts();
if (accounts.length === 0) {
// This test needs an account for deployment. We create one in case there is none available in the RPC server.
const privateKey = PrivateKey.fromString(privateKeyString);
await getUnsafeSchnorrAccount(client, privateKey).waitDeploy();
accounts = await client.getAccounts();
}
const owner = accounts[0];
const tx = new DeployMethod(owner.publicKey, client, PrivateTokenContractAbi, [
initialBalance,
owner.address,
]).send();
await tx.wait();
const receipt = await tx.getReceipt();
console.log(`Contract Deployed: ${receipt.contractAddress}`);
return receipt.txHash.toString();
},
SANDBOX_URL,
privKey.toString(),
initialBalance,
PrivateTokenContractAbi,
);

const txResult = await testClient.getTxReceipt(AztecJs.TxHash.fromString(txHash));
expect(txResult.status).toEqual(AztecJs.TxStatus.MINED);
contractAddress = txResult.contractAddress!;
};

const getPrivateTokenAddress = async () => {
if (!contractAddress) {
await deployPrivateTokenContract();
}
return contractAddress;
};
});
Loading

0 comments on commit 7f4fa43

Please sign in to comment.