Skip to content

Commit cd47f7c

Browse files
committed
Use ava for UI tests
1 parent 5b531a5 commit cd47f7c

File tree

9 files changed

+115
-286
lines changed

9 files changed

+115
-286
lines changed

package.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@
2929
]
3030
},
3131
"devDependencies": {
32-
"tsx": "^4.19.2",
3332
"@eslint/js": "^9.21.0",
3433
"concurrently": "^9.1.2",
3534
"eslint": "^9.33.0",

packages/ui/ava.config.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
module.exports = {
2+
extensions: ['ts'],
3+
require: ['./ts-node-register.cjs'],
4+
timeout: '10m',
5+
workerThreads: false,
6+
};

packages/ui/package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,10 @@
1010
"dev:api": "ENV=dev deno task dev",
1111
"start": "sirv public",
1212
"validate": "svelte-check",
13-
"test": "node --import tsx --test ./src/**/*.node.test.ts"
13+
"test": "ava"
1414
},
1515
"devDependencies": {
16+
"ava": "^6.1.2",
1617
"@rollup/plugin-alias": "^5.0.0",
1718
"@rollup/plugin-commonjs": "^28.0.0",
1819
"@rollup/plugin-json": "^6.0.0",
@@ -40,6 +41,7 @@
4041
"svelte-preprocess": "^5.0.0",
4142
"tailwindcss": "^3.0.15",
4243
"tslib": "^2.0.0",
44+
"ts-node": "^10.9.2",
4345
"typescript": "^5.0.0"
4446
},
4547
"dependencies": {

packages/ui/src/solidity/remix.node.test.ts

Lines changed: 0 additions & 96 deletions
This file was deleted.
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
import test from 'ava';
2+
import { remixURL } from './remix';
3+
4+
// Decoder used in Remix
5+
const decodeBase64 = (b64Payload: string) => {
6+
const raw = atob(decodeURIComponent(b64Payload));
7+
const bytes = Uint8Array.from(raw, c => c.charCodeAt(0));
8+
return new TextDecoder().decode(bytes);
9+
};
10+
11+
test('remixURL encodes code param decodable by decodeBase64', t => {
12+
const contractSource = 'contract A{}';
13+
14+
const url = remixURL(contractSource);
15+
const codeParam = url.searchParams.get('code');
16+
t.truthy(codeParam, 'Expected code search param to be set');
17+
18+
const decoded = decodeBase64(codeParam!);
19+
t.is(decoded, contractSource, 'Decoded code should equal original source');
20+
});
21+
22+
test('remixURL sets deployProxy flag when upgradeable', t => {
23+
const contractSource = 'contract A{}';
24+
25+
const urlTrue = remixURL(contractSource, true);
26+
t.is(urlTrue.searchParams.get('deployProxy'), 'true');
27+
28+
const urlFalse = remixURL(contractSource, false);
29+
t.is(urlFalse.searchParams.get('deployProxy'), null);
30+
});
31+
32+
test('remixURL encodes code param with special characters decodable by decodeBase64', t => {
33+
// not a valid contract
34+
const contractSource = `// SPDX-License-Identifier: MIT
35+
// Compatible with OpenZeppelin Contracts ^5.4.0
36+
pragma solidity ^0.8.27;
37+
38+
import {AccountERC7579} from "@openzeppelin/contracts/account/extensions/draft-AccountERC7579.sol";
39+
import {ERC7739} from "@openzeppelin/contracts/utils/cryptography/signers/draft-ERC7739.sol";
40+
41+
contract MyAccount is ERC7739, AccountERC7579 {
42+
constructor(address signer)
43+
EIP712(unicode"MyAccount🌾", "1")
44+
SignerECDSA(signer)
45+
{}
46+
47+
function isValidSignature(bytes32 hash, bytes calldata signature)
48+
public
49+
view
50+
override(AccountERC7579, ERC7739)
51+
returns (bytes4)
52+
{
53+
// ERC-7739 can return the ERC-1271 magic value, 0xffffffff (invalid) or 0x77390001 (detection).
54+
// If the returned value is 0xffffffff, fallback to ERC-7579 validation.
55+
bytes4 erc7739magic = ERC7739.isValidSignature(hash, signature);
56+
return erc7739magic == bytes4(0xffffffff) ? AccountERC7579.isValidSignature(hash, signature) : erc7739magic;
57+
}
58+
}`;
59+
60+
const url = remixURL(contractSource);
61+
const codeParam = url.searchParams.get('code');
62+
t.truthy(codeParam, 'Expected code search param to be set');
63+
64+
const decoded = decodeBase64(codeParam!);
65+
t.is(decoded, contractSource, 'Decoded code should equal original source');
66+
});

packages/ui/src/solidity/remix.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,17 @@
11
export function remixURL(code: string, upgradeable = false): URL {
22
const remix = new URL('https://remix.ethereum.org');
33

4-
const encodedCode = btoa(String.fromCharCode(...new TextEncoder().encode(code))).replace(/=*$/, '');
4+
// Encode to base64 in a way that works in both browser and Node.
5+
const encodedCode = ((): string => {
6+
// Prefer browser btoa when available
7+
if (typeof (globalThis as any).btoa === 'function') {
8+
const bytes = new TextEncoder().encode(code);
9+
const binary = String.fromCharCode(...bytes);
10+
return (globalThis as any).btoa(binary).replace(/=*$/, '');
11+
}
12+
// Fallback to Node Buffer
13+
return Buffer.from(code, 'utf8').toString('base64').replace(/=*$/, '');
14+
})();
515

616
remix.searchParams.set('code', encodedCode);
717

packages/ui/ts-node-register.cjs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
/* eslint-disable */
2+
3+
// Configure ts-node to use a test-specific tsconfig
4+
const path = require('path');
5+
6+
require('ts-node').register({
7+
transpileOnly: true,
8+
project: path.join(__dirname, 'tsconfig.test.json'),
9+
});

packages/ui/tsconfig.test.json

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
{
2+
"extends": "./tsconfig.json",
3+
"compilerOptions": {
4+
"module": "NodeNext",
5+
"moduleResolution": "NodeNext",
6+
"noEmit": true,
7+
"lib": [
8+
"es2020",
9+
"dom",
10+
"dom.iterable"
11+
]
12+
},
13+
"include": [
14+
"src/**/*.ts"
15+
]
16+
}
17+

0 commit comments

Comments
 (0)