@acala-network/chopsticks-testing 0.6.2 → 0.6.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/check.d.ts ADDED
@@ -0,0 +1,37 @@
1
+ import { ApiPromise } from '@polkadot/api';
2
+ import { Codec } from '@polkadot/types/types';
3
+ export type EventFilter = string | {
4
+ method: string;
5
+ section: string;
6
+ };
7
+ export type RedactOptions = {
8
+ number?: boolean | number;
9
+ hash?: boolean;
10
+ hex?: boolean;
11
+ address?: boolean;
12
+ };
13
+ export declare class Checker {
14
+ #private;
15
+ constructor(value: any, message?: string);
16
+ toHuman(): this;
17
+ toHex(): this;
18
+ toJson(): this;
19
+ message(message: string): this;
20
+ filterEvents(...filters: EventFilter[]): this;
21
+ redact(options?: RedactOptions): this;
22
+ map(fn: (value: any) => any): this;
23
+ pipe(fn?: (value: Checker) => Checker): Checker;
24
+ value(): Promise<any>;
25
+ toMatchSnapshot(msg?: string): Promise<void>;
26
+ }
27
+ export declare const check: (value: any, msg?: string) => Checker;
28
+ type Api = {
29
+ api: ApiPromise;
30
+ };
31
+ export declare const checkEvents: ({ events }: {
32
+ events: Promise<Codec[] | Codec>;
33
+ }, ...filters: EventFilter[]) => Checker;
34
+ export declare const checkSystemEvents: ({ api }: Api, ...filters: EventFilter[]) => Checker;
35
+ export declare const checkUmp: ({ api }: Api) => Checker;
36
+ export declare const checkHrmp: ({ api }: Api) => Checker;
37
+ export {};
package/lib/check.js ADDED
@@ -0,0 +1,170 @@
1
+ import { expect } from 'vitest';
2
+ const processCodecOrArray = (codec, fn) => Array.isArray(codec) ? codec.map(fn) : fn(codec);
3
+ const toHuman = (codec) => processCodecOrArray(codec, (c) => c?.toHuman?.() ?? c);
4
+ const toHex = (codec) => processCodecOrArray(codec, (c) => c?.toHex?.() ?? c);
5
+ const toJson = (codec) => processCodecOrArray(codec, (c) => c?.toJSON?.() ?? c);
6
+ export class Checker {
7
+ #value;
8
+ #pipeline = [];
9
+ #format = 'json';
10
+ #message;
11
+ #redactOptions;
12
+ constructor(value, message) {
13
+ this.#value = value;
14
+ this.#message = message;
15
+ }
16
+ toHuman() {
17
+ this.#format = 'human';
18
+ return this;
19
+ }
20
+ toHex() {
21
+ this.#format = 'hex';
22
+ return this;
23
+ }
24
+ toJson() {
25
+ this.#format = 'json';
26
+ return this;
27
+ }
28
+ message(message) {
29
+ this.#message = message;
30
+ return this;
31
+ }
32
+ filterEvents(...filters) {
33
+ this.toHuman();
34
+ this.#pipeline.push((value) => {
35
+ let data = value.map(({ event: { index: _, ...event } }) => event);
36
+ if (filters.length > 0) {
37
+ data = data.filter((evt) => {
38
+ return filters.some((filter) => {
39
+ if (typeof filter === 'string') {
40
+ return evt.section === filter;
41
+ }
42
+ else if ('method' in filter) {
43
+ const { section, method } = filter;
44
+ return evt.section === section && evt.method === method;
45
+ }
46
+ });
47
+ });
48
+ }
49
+ return data;
50
+ });
51
+ return this;
52
+ }
53
+ redact(options = { number: 2, hash: true }) {
54
+ this.#redactOptions = {
55
+ ...this.#redactOptions,
56
+ ...options,
57
+ };
58
+ return this;
59
+ }
60
+ #redact(value) {
61
+ if (!this.#redactOptions) {
62
+ return value;
63
+ }
64
+ const redactNumber = this.#redactOptions.number === true || typeof this.#redactOptions.number === 'number';
65
+ const precision = redactNumber
66
+ ? typeof this.#redactOptions.number === 'number'
67
+ ? this.#redactOptions.number
68
+ : 0
69
+ : 0;
70
+ const redactHash = this.#redactOptions.hash === true;
71
+ const redactHex = this.#redactOptions.hex === true;
72
+ const redactAddress = this.#redactOptions.address === true;
73
+ const processNumber = (value) => {
74
+ if (precision > 0) {
75
+ const rounded = parseFloat(value.toPrecision(precision));
76
+ if (rounded === value) {
77
+ return rounded;
78
+ }
79
+ return `(rounded ${rounded})`;
80
+ }
81
+ return '(number)';
82
+ };
83
+ const process = (obj) => {
84
+ if (obj == null) {
85
+ return obj;
86
+ }
87
+ if (Array.isArray(obj)) {
88
+ return obj.map(process);
89
+ }
90
+ if (redactNumber && typeof obj === 'number') {
91
+ return processNumber(obj);
92
+ }
93
+ if (typeof obj === 'string') {
94
+ if (redactNumber && obj.match(/0x000000[0-9a-f]{26}/)) {
95
+ // this is very likely u128 encoded in hex
96
+ const num = parseInt(obj);
97
+ return processNumber(num);
98
+ }
99
+ if (redactHash && obj.match(/0x[0-9a-f]{64}/)) {
100
+ return '(hash)';
101
+ }
102
+ if (redactHex && obj.match(/0x[0-9a-f]+/)) {
103
+ return '(hex)';
104
+ }
105
+ if (redactAddress && obj.match(/^[1-9A-HJ-NP-Za-km-z]{46,48}$/)) {
106
+ return '(address)';
107
+ }
108
+ if (redactNumber && obj.match(/^-?[\d,]+$/)) {
109
+ const num = parseInt(obj.replace(/,/g, ''));
110
+ return processNumber(num);
111
+ }
112
+ return obj;
113
+ }
114
+ if (typeof obj === 'object') {
115
+ return Object.fromEntries(Object.entries(obj).map(([k, v]) => [k, process(v)]));
116
+ }
117
+ return obj;
118
+ };
119
+ return process(value);
120
+ }
121
+ map(fn) {
122
+ this.#pipeline.push(fn);
123
+ return this;
124
+ }
125
+ pipe(fn) {
126
+ return fn ? fn(this) : this;
127
+ }
128
+ async value() {
129
+ let value = await this.#value;
130
+ switch (this.#format) {
131
+ case 'human':
132
+ value = toHuman(value);
133
+ break;
134
+ case 'hex':
135
+ value = toHex(value);
136
+ break;
137
+ case 'json':
138
+ value = toJson(value);
139
+ break;
140
+ }
141
+ for (const fn of this.#pipeline) {
142
+ value = await fn(value);
143
+ }
144
+ value = this.#redact(value);
145
+ return value;
146
+ }
147
+ async toMatchSnapshot(msg) {
148
+ return expect(await this.value()).toMatchSnapshot(msg ?? this.#message);
149
+ }
150
+ }
151
+ export const check = (value, msg) => {
152
+ if (value instanceof Checker) {
153
+ if (msg) {
154
+ return value.message(msg);
155
+ }
156
+ return value;
157
+ }
158
+ return new Checker(value, msg);
159
+ };
160
+ export const checkEvents = ({ events }, ...filters) => check(events, 'events')
161
+ .filterEvents(...filters)
162
+ .redact();
163
+ export const checkSystemEvents = ({ api }, ...filters) => check(api.query.system.events(), 'system events')
164
+ .filterEvents(...filters)
165
+ .redact();
166
+ export const checkUmp = ({ api }) => check(api.query.parachainSystem.upwardMessages(), 'ump').map((value) => api.createType('Vec<XcmVersionedXcm>', value).toJSON());
167
+ export const checkHrmp = ({ api }) => check(api.query.parachainSystem.hrmpOutboundMessages(), 'hrmp').map((value) => value.map(({ recipient, data }) => ({
168
+ data: api.createType('(XcmpMessageFormat, XcmVersionedXcm)', data),
169
+ recipient,
170
+ })));
package/lib/index.d.ts CHANGED
@@ -4,6 +4,7 @@ import { HexString } from '@polkadot/util/types';
4
4
  import { Keyring } from '@polkadot/keyring';
5
5
  import { StorageValues } from '@acala-network/chopsticks/utils/set-storage';
6
6
  import { SubmittableExtrinsic } from '@polkadot/api-base/types';
7
+ export * from './check';
7
8
  export type SetupOption = {
8
9
  endpoint: string;
9
10
  blockNumber?: number;
@@ -13,6 +14,7 @@ export type SetupOption = {
13
14
  timeout?: number;
14
15
  };
15
16
  export declare const setupContext: ({ endpoint, blockNumber, blockHash, wasmOverride, db, timeout }: SetupOption) => Promise<{
17
+ url: string;
16
18
  chain: import("@acala-network/chopsticks").Blockchain;
17
19
  ws: WsProvider;
18
20
  api: ApiPromise;
@@ -26,14 +28,11 @@ export declare const setupContext: ({ endpoint, blockNumber, blockHash, wasmOver
26
28
  setHead: (hashOrNumber: string | number) => Promise<any>;
27
29
  };
28
30
  teardown(): Promise<void>;
31
+ pause(): Promise<unknown>;
29
32
  }>;
30
33
  type CodecOrArray = Codec | Codec[];
31
- export declare const expectEvent: (codec: CodecOrArray, event: any) => void;
32
- export declare const expectHuman: (codec: CodecOrArray) => Vi.Assertion<any>;
33
34
  export declare const expectJson: (codec: CodecOrArray) => Vi.Assertion<any>;
34
35
  export declare const expectHex: (codec: CodecOrArray) => Vi.Assertion<any>;
35
- export declare const matchJson: (codec: CodecOrArray) => void;
36
- export declare const matchHuman: (codec: CodecOrArray) => void;
37
36
  type EventFilter = string | {
38
37
  method: string;
39
38
  section: string;
@@ -42,12 +41,6 @@ export declare const matchEvents: (events: Promise<Codec[] | Codec>, ...filters:
42
41
  export declare const matchSystemEvents: ({ api }: {
43
42
  api: ApiPromise;
44
43
  }, ...filters: EventFilter[]) => Promise<void>;
45
- export declare const matchUmp: ({ api }: {
46
- api: ApiPromise;
47
- }) => Promise<void>;
48
- export declare const matchHrmp: ({ api }: {
49
- api: ApiPromise;
50
- }) => Promise<void>;
51
44
  export declare const redact: (data: any | Promise<any>) => Promise<any>;
52
45
  export declare function defer<T>(): {
53
46
  resolve: (value: any) => void;
@@ -57,15 +50,17 @@ export declare function defer<T>(): {
57
50
  export declare const sendTransaction: (tx: Promise<SubmittableExtrinsic<'promise'>>) => Promise<{
58
51
  events: Promise<Codec[]>;
59
52
  }>;
60
- export declare const balance: (api: ApiPromise, address: string) => Promise<Record<string, import("@polkadot/types/types").AnyJson>>;
61
- export declare const testingPairs: (ss58Format?: number) => {
53
+ export declare const testingPairs: (keyringType?: 'ed25519' | 'sr25519', ss58Format?: number) => {
62
54
  alice: import("@polkadot/keyring/types").KeyringPair;
63
55
  bob: import("@polkadot/keyring/types").KeyringPair;
64
56
  charlie: import("@polkadot/keyring/types").KeyringPair;
65
57
  dave: import("@polkadot/keyring/types").KeyringPair;
66
58
  eve: import("@polkadot/keyring/types").KeyringPair;
67
- test1: import("@polkadot/keyring/types").KeyringPair;
68
- test2: import("@polkadot/keyring/types").KeyringPair;
59
+ alith: import("@polkadot/keyring/types").KeyringPair;
60
+ baltathar: import("@polkadot/keyring/types").KeyringPair;
61
+ charleth: import("@polkadot/keyring/types").KeyringPair;
62
+ dorothy: import("@polkadot/keyring/types").KeyringPair;
63
+ ethan: import("@polkadot/keyring/types").KeyringPair;
69
64
  keyring: Keyring;
65
+ keyringEth: import("@polkadot/keyring/types").KeyringInstance;
70
66
  };
71
- export {};
package/lib/index.js CHANGED
@@ -1,7 +1,8 @@
1
1
  import { ApiPromise, WsProvider } from '@polkadot/api';
2
2
  import { BuildBlockMode, setupWithServer } from '@acala-network/chopsticks';
3
- import { Keyring } from '@polkadot/keyring';
3
+ import { Keyring, createTestKeyring } from '@polkadot/keyring';
4
4
  import { expect } from 'vitest';
5
+ export * from './check';
5
6
  export const setupContext = async ({ endpoint, blockNumber, blockHash, wasmOverride, db, timeout }) => {
6
7
  // random port
7
8
  const port = Math.floor(Math.random() * 10000) + 10000;
@@ -15,7 +16,8 @@ export const setupContext = async ({ endpoint, blockNumber, blockHash, wasmOverr
15
16
  'wasm-override': wasmOverride,
16
17
  };
17
18
  const { chain, listenPort, close } = await setupWithServer(config);
18
- const ws = new WsProvider(`ws://localhost:${listenPort}`, undefined, undefined, timeout);
19
+ const url = `ws://localhost:${listenPort}`;
20
+ const ws = new WsProvider(url, undefined, undefined, timeout);
19
21
  const api = await ApiPromise.create({
20
22
  provider: ws,
21
23
  signedExtensions: {
@@ -27,6 +29,7 @@ export const setupContext = async ({ endpoint, blockNumber, blockHash, wasmOverr
27
29
  });
28
30
  await api.isReady;
29
31
  return {
32
+ url,
30
33
  chain,
31
34
  ws,
32
35
  api,
@@ -48,30 +51,24 @@ export const setupContext = async ({ endpoint, blockNumber, blockHash, wasmOverr
48
51
  await api.disconnect();
49
52
  await close();
50
53
  },
54
+ async pause() {
55
+ await ws.send('dev_setBlockBuildMode', [BuildBlockMode.Instant]);
56
+ // log a bit later to ensure the message is visible
57
+ setTimeout(() => console.log(`Test paused. Polkadot.js apps URL: https://polkadot.js.org/apps/?rpc=${url}`), 100);
58
+ return new Promise((_resolve) => { }); // wait forever
59
+ },
51
60
  };
52
61
  };
53
62
  const processCodecOrArray = (codec, fn) => Array.isArray(codec) ? codec.map(fn) : fn(codec);
54
63
  const toHuman = (codec) => processCodecOrArray(codec, (c) => c?.toHuman?.() ?? c);
55
64
  const toJson = (codec) => processCodecOrArray(codec, (c) => c?.toJSON?.() ?? c);
56
65
  const toHex = (codec) => processCodecOrArray(codec, (c) => c?.toHex?.() ?? c);
57
- export const expectEvent = (codec, event) => {
58
- return expect(toHuman(codec)).toEqual(expect.arrayContaining([expect.objectContaining(event)]));
59
- };
60
- export const expectHuman = (codec) => {
61
- return expect(toHuman(codec));
62
- };
63
66
  export const expectJson = (codec) => {
64
67
  return expect(toJson(codec));
65
68
  };
66
69
  export const expectHex = (codec) => {
67
70
  return expect(toHex(codec));
68
71
  };
69
- export const matchJson = (codec) => {
70
- return expect(toJson(codec)).toMatchSnapshot();
71
- };
72
- export const matchHuman = (codec) => {
73
- return expect(toHuman(codec)).toMatchSnapshot();
74
- };
75
72
  const _matchEvents = async (msg, events, ...filters) => {
76
73
  let data = toHuman(await events).map(({ event: { index: _, ...event } }) => event);
77
74
  if (filters.length > 0) {
@@ -94,19 +91,6 @@ export const matchEvents = async (events, ...filters) => {
94
91
  export const matchSystemEvents = async ({ api }, ...filters) => {
95
92
  await _matchEvents('system events', redact(api.query.system.events()), ...filters);
96
93
  };
97
- export const matchUmp = async ({ api }) => {
98
- const ump = await api.query.parachainSystem.upwardMessages();
99
- const decoded = ump.toJSON().map((msg) => api.createType('XcmVersionedXcm', msg));
100
- expect(decoded).toMatchSnapshot('ump');
101
- };
102
- export const matchHrmp = async ({ api }) => {
103
- const hrmp = await api.query.parachainSystem.hrmpOutboundMessages();
104
- const decoded = hrmp.toJSON().map(({ recipient, data }) => ({
105
- data: api.createType('(XcmpMessageFormat, XcmVersionedXcm)', data),
106
- recipient,
107
- }));
108
- expect(decoded).toMatchSnapshot('hrmp');
109
- };
110
94
  export const redact = async (data) => {
111
95
  const json = toHuman(await data);
112
96
  const process = (obj) => {
@@ -156,7 +140,8 @@ export const sendTransaction = async (tx) => {
156
140
  const signed = await tx;
157
141
  const deferred = defer();
158
142
  await signed.send((status) => {
159
- if (status.isCompleted) {
143
+ console.log('tranaction status: ', status.status.toHuman());
144
+ if (status.isInBlock || status.isFinalized) {
160
145
  deferred.resolve(status.events);
161
146
  }
162
147
  if (status.isError) {
@@ -167,27 +152,22 @@ export const sendTransaction = async (tx) => {
167
152
  events: deferred.promise,
168
153
  };
169
154
  };
170
- export const balance = async (api, address) => {
171
- const account = await api.query.system.account(address);
172
- return account.data.toJSON();
173
- };
174
- export const testingPairs = (ss58Format) => {
175
- const keyring = new Keyring({ type: 'ed25519', ss58Format }); // cannot use sr25519 as it is non determinstic
176
- const alice = keyring.addFromUri('//Alice');
177
- const bob = keyring.addFromUri('//Bob');
178
- const charlie = keyring.addFromUri('//Charlie');
179
- const dave = keyring.addFromUri('//Dave');
180
- const eve = keyring.addFromUri('//Eve');
181
- const test1 = keyring.addFromUri('//test1');
182
- const test2 = keyring.addFromUri('//test2');
155
+ export const testingPairs = (keyringType = 'ed25519', ss58Format) => {
156
+ const keyringEth = createTestKeyring({ type: 'ethereum' });
157
+ // default to ed25519 because sr25519 signature is non-deterministic
158
+ const keyring = new Keyring({ type: keyringType, ss58Format });
183
159
  return {
184
- alice,
185
- bob,
186
- charlie,
187
- dave,
188
- eve,
189
- test1,
190
- test2,
160
+ alice: keyring.addFromUri('//Alice'),
161
+ bob: keyring.addFromUri('//Bob'),
162
+ charlie: keyring.addFromUri('//Charlie'),
163
+ dave: keyring.addFromUri('//Dave'),
164
+ eve: keyring.addFromUri('//Eve'),
165
+ alith: keyringEth.getPair('0xf24FF3a9CF04c71Dbc94D0b566f7A27B94566cac'),
166
+ baltathar: keyringEth.getPair('0x3Cd0A705a2DC65e5b1E1205896BaA2be8A07c6e0'),
167
+ charleth: keyringEth.getPair('0x798d4Ba9baf0064Ec19eB4F0a1a45785ae9D6DFc'),
168
+ dorothy: keyringEth.getPair('0x773539d4Ac0e786233D90A233654ccEE26a613D9'),
169
+ ethan: keyringEth.getPair('0xFf64d3F6efE2317EE2807d223a0Bdc4c0c49dfDB'),
191
170
  keyring,
171
+ keyringEth,
192
172
  };
193
173
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@acala-network/chopsticks-testing",
3
- "version": "0.6.2",
3
+ "version": "0.6.4",
4
4
  "author": "Bryan Chen <xlchen1291@gmail.com>",
5
5
  "license": "Apache-2.0",
6
6
  "scripts": {
@@ -8,14 +8,14 @@
8
8
  "build": "tsc -p ./tsconfig.json"
9
9
  },
10
10
  "dependencies": {
11
- "@acala-network/chopsticks": "0.6.2"
11
+ "@acala-network/chopsticks": "0.6.4"
12
12
  },
13
13
  "peerDependencies": {
14
- "vitest": "^0.29.2"
14
+ "vitest": "^0.30.1"
15
15
  },
16
16
  "devDependencies": {
17
- "@types/chai": "^4.3.4",
18
- "typescript": "^5.0.3"
17
+ "@types/chai": "^4.3.5",
18
+ "typescript": "^5.0.4"
19
19
  },
20
20
  "files": [
21
21
  "lib"