@avalix/chroma 0.0.0 → 0.0.2
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/README.md +2 -2
- package/dist/index.d.ts +40 -0
- package/dist/index.js +139 -0
- package/package.json +34 -30
package/README.md
CHANGED
|
@@ -15,7 +15,7 @@ npm install @avalix/chroma @playwright/test
|
|
|
15
15
|
### Using Default Polkadot JS Wallet
|
|
16
16
|
|
|
17
17
|
```typescript
|
|
18
|
-
import {
|
|
18
|
+
import { expect, test } from '@avalix/chroma'
|
|
19
19
|
|
|
20
20
|
test('should connect wallet and sign transaction', async ({ page, importAccount, authorize, approveTx }) => {
|
|
21
21
|
// Import a test account
|
|
@@ -58,7 +58,7 @@ const talismanTest = createWalletTest({
|
|
|
58
58
|
// Create test with custom extension path
|
|
59
59
|
const customTest = createWalletTest({
|
|
60
60
|
walletType: 'polkadot-js',
|
|
61
|
-
walletConfig: {
|
|
61
|
+
walletConfig: {
|
|
62
62
|
customPath: './my-custom-extension'
|
|
63
63
|
}
|
|
64
64
|
})
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { BrowserContext, BrowserContext as BrowserContext$1, Page, Page as Page$1, expect, test as test$1 } from "@playwright/test";
|
|
2
|
+
|
|
3
|
+
//#region src/context-playwright/index.d.ts
|
|
4
|
+
type WalletType = "polkadot-js" | "talisman";
|
|
5
|
+
interface WalletConfig {
|
|
6
|
+
downloadUrl?: string;
|
|
7
|
+
customPath?: string;
|
|
8
|
+
}
|
|
9
|
+
interface WalletAccount {
|
|
10
|
+
seed: string;
|
|
11
|
+
name?: string;
|
|
12
|
+
password?: string;
|
|
13
|
+
}
|
|
14
|
+
interface ChromaTestOptions {
|
|
15
|
+
walletType?: WalletType;
|
|
16
|
+
walletConfig?: WalletConfig;
|
|
17
|
+
headless?: boolean;
|
|
18
|
+
slowMo?: number;
|
|
19
|
+
}
|
|
20
|
+
interface ExtendedPage extends Page$1 {
|
|
21
|
+
__extensionContext: BrowserContext$1;
|
|
22
|
+
__extensionId: string;
|
|
23
|
+
}
|
|
24
|
+
interface WalletFixtures {
|
|
25
|
+
page: ExtendedPage;
|
|
26
|
+
walletType: WalletType;
|
|
27
|
+
walletConfig: WalletConfig;
|
|
28
|
+
importAccount: (options: WalletAccount) => Promise<void>;
|
|
29
|
+
authorize: () => Promise<void>;
|
|
30
|
+
approveTx: (options?: {
|
|
31
|
+
password?: string;
|
|
32
|
+
}) => Promise<void>;
|
|
33
|
+
}
|
|
34
|
+
declare function createWalletTest(options?: ChromaTestOptions): ReturnType<typeof test$1.extend<WalletFixtures>>;
|
|
35
|
+
declare const test: ReturnType<typeof createWalletTest>;
|
|
36
|
+
//#endregion
|
|
37
|
+
//#region src/context-playwright/download-polkadot-js.d.ts
|
|
38
|
+
declare function downloadAndExtractPolkadotExtension(targetDir?: string): Promise<string>;
|
|
39
|
+
//#endregion
|
|
40
|
+
export { type BrowserContext, type ChromaTestOptions, type Page, type WalletAccount, type WalletConfig, type WalletFixtures, type WalletType, createWalletTest, downloadAndExtractPolkadotExtension, expect, test };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import { chromium, expect, test as test$1 } from "@playwright/test";
|
|
2
|
+
import fs, { createWriteStream } from "node:fs";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import process from "node:process";
|
|
5
|
+
import { pipeline } from "node:stream/promises";
|
|
6
|
+
import { Extract } from "unzipper";
|
|
7
|
+
|
|
8
|
+
//#region src/context-playwright/download-polkadot-js.ts
|
|
9
|
+
const POLKADOT_JS_EXTENSION = "https://github.com/polkadot-js/extension/releases/download/v0.61.7/master-chrome-build.zip";
|
|
10
|
+
async function downloadAndExtractPolkadotExtension(targetDir) {
|
|
11
|
+
const extensionsDir = targetDir || path.resolve(process.cwd(), ".chroma");
|
|
12
|
+
const extensionDir = path.join(extensionsDir, "polkadot-extension-chrome");
|
|
13
|
+
const zipPath = path.join(extensionsDir, "polkadot-extension.zip");
|
|
14
|
+
await fs.promises.mkdir(extensionsDir, { recursive: true });
|
|
15
|
+
if (fs.existsSync(extensionDir) && fs.readdirSync(extensionDir).length > 0) {
|
|
16
|
+
console.log("✅ Polkadot extension already exists at:", extensionDir);
|
|
17
|
+
return extensionDir;
|
|
18
|
+
}
|
|
19
|
+
try {
|
|
20
|
+
console.log("📥 Downloading Polkadot JS extension...");
|
|
21
|
+
const response = await fetch(POLKADOT_JS_EXTENSION);
|
|
22
|
+
if (!response.ok) throw new Error(`Failed to download extension: ${response.status} ${response.statusText}`);
|
|
23
|
+
const writeStream = createWriteStream(zipPath);
|
|
24
|
+
await pipeline(response.body, writeStream);
|
|
25
|
+
console.log("📦 Extracting extension...");
|
|
26
|
+
await pipeline(fs.createReadStream(zipPath), Extract({ path: extensionDir }));
|
|
27
|
+
await fs.promises.unlink(zipPath);
|
|
28
|
+
console.log("✅ Polkadot extension downloaded and extracted to:", extensionDir);
|
|
29
|
+
return extensionDir;
|
|
30
|
+
} catch (error) {
|
|
31
|
+
if (fs.existsSync(zipPath)) await fs.promises.unlink(zipPath).catch(() => {});
|
|
32
|
+
if (fs.existsSync(extensionDir)) await fs.promises.rmdir(extensionDir, { recursive: true }).catch(() => {});
|
|
33
|
+
throw new Error(`Failed to download/extract Polkadot extension: ${error instanceof Error ? error.message : String(error)}`);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
//#endregion
|
|
38
|
+
//#region src/context-playwright/index.ts
|
|
39
|
+
const DEFAULT_WALLET_CONFIGS = {
|
|
40
|
+
"polkadot-js": { downloadUrl: "https://github.com/polkadot-js/extension/releases/download/v0.61.7/master-chrome-build.zip" },
|
|
41
|
+
"talisman": {}
|
|
42
|
+
};
|
|
43
|
+
async function getExtensionPath(walletType, walletConfig) {
|
|
44
|
+
const { customPath } = walletConfig;
|
|
45
|
+
if (customPath) return customPath;
|
|
46
|
+
switch (walletType) {
|
|
47
|
+
case "polkadot-js": return await downloadAndExtractPolkadotExtension();
|
|
48
|
+
case "talisman": throw new Error("Talisman wallet download not implemented yet");
|
|
49
|
+
default: throw new Error(`Unsupported wallet type: ${walletType}`);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
async function findExtensionPopup(context, extensionId) {
|
|
53
|
+
const pages = context.pages();
|
|
54
|
+
for (const p of pages) if (p.url().includes(`chrome-extension://${extensionId}/`)) return p;
|
|
55
|
+
throw new Error(`Extension popup not found for ID: ${extensionId}`);
|
|
56
|
+
}
|
|
57
|
+
function createWalletTest(options = {}) {
|
|
58
|
+
const { walletType = "polkadot-js", walletConfig, headless = false, slowMo = 150 } = options;
|
|
59
|
+
const finalWalletConfig = walletConfig || DEFAULT_WALLET_CONFIGS[walletType];
|
|
60
|
+
return test$1.extend({
|
|
61
|
+
walletType: async ({}, use) => {
|
|
62
|
+
await use(walletType);
|
|
63
|
+
},
|
|
64
|
+
walletConfig: async ({}, use) => {
|
|
65
|
+
await use(finalWalletConfig);
|
|
66
|
+
},
|
|
67
|
+
page: async ({}, use) => {
|
|
68
|
+
const extensionPath = await getExtensionPath(walletType, finalWalletConfig);
|
|
69
|
+
const context = await chromium.launchPersistentContext("", {
|
|
70
|
+
headless,
|
|
71
|
+
args: [`--load-extension=${extensionPath}`, `--disable-extensions-except=${extensionPath}`],
|
|
72
|
+
slowMo
|
|
73
|
+
});
|
|
74
|
+
const page = context.pages()[0] || await context.newPage();
|
|
75
|
+
const extendedPage = page;
|
|
76
|
+
extendedPage.__extensionContext = context;
|
|
77
|
+
let [background] = context.serviceWorkers();
|
|
78
|
+
if (!background) background = await context.waitForEvent("serviceworker");
|
|
79
|
+
extendedPage.__extensionId = background.url().split("/")[2];
|
|
80
|
+
await use(page);
|
|
81
|
+
await context.close();
|
|
82
|
+
},
|
|
83
|
+
importAccount: async ({ page }, use) => {
|
|
84
|
+
const importAccount = async ({ seed, name = "Test Account", password = "h3llop0lkadot!" }) => {
|
|
85
|
+
const context = page.__extensionContext;
|
|
86
|
+
const extensionPopupUrl = `chrome-extension://${page.__extensionId}/index.html`;
|
|
87
|
+
const extensionPage = await context.newPage();
|
|
88
|
+
try {
|
|
89
|
+
await extensionPage.goto(extensionPopupUrl);
|
|
90
|
+
const understoodButton = extensionPage.getByRole("button", { name: "Understood, let me continue" });
|
|
91
|
+
if (await understoodButton.count() > 0) {
|
|
92
|
+
await understoodButton.click();
|
|
93
|
+
await extensionPage.waitForTimeout(100);
|
|
94
|
+
}
|
|
95
|
+
await extensionPage.goto(`${extensionPopupUrl}#/account/import-seed`);
|
|
96
|
+
await extensionPage.locator("textarea").fill(seed);
|
|
97
|
+
await extensionPage.locator("button:has-text(\"Next\")").click();
|
|
98
|
+
await extensionPage.locator("input[type=\"text\"]").fill(name);
|
|
99
|
+
await extensionPage.locator("input[type=\"password\"]").fill(password);
|
|
100
|
+
await extensionPage.locator("div").filter({ hasText: /^Repeat password for verification$/ }).getByRole("textbox").fill(password);
|
|
101
|
+
await extensionPage.getByRole("button", { name: "Add the account with the supplied seed" }).click();
|
|
102
|
+
console.log(`✅ Created wallet account: ${name}`);
|
|
103
|
+
} finally {
|
|
104
|
+
await extensionPage.close();
|
|
105
|
+
}
|
|
106
|
+
};
|
|
107
|
+
await use(importAccount);
|
|
108
|
+
},
|
|
109
|
+
authorize: async ({ page }, use) => {
|
|
110
|
+
const authorize = async () => {
|
|
111
|
+
const context = page.__extensionContext;
|
|
112
|
+
const extensionId = page.__extensionId;
|
|
113
|
+
await new Promise((resolve) => setTimeout(resolve, 1e3));
|
|
114
|
+
const extensionPopup = await findExtensionPopup(context, extensionId);
|
|
115
|
+
await extensionPopup.getByText("Select all").click();
|
|
116
|
+
await extensionPopup.getByRole("button", { name: /Connect \d+ account\(s\)/ }).click();
|
|
117
|
+
console.log("✅ Wallet connected successfully");
|
|
118
|
+
};
|
|
119
|
+
await use(authorize);
|
|
120
|
+
},
|
|
121
|
+
approveTx: async ({ page }, use) => {
|
|
122
|
+
const approveTx = async (options$1 = {}) => {
|
|
123
|
+
const { password = "h3llop0lkadot!" } = options$1;
|
|
124
|
+
const context = page.__extensionContext;
|
|
125
|
+
const extensionId = page.__extensionId;
|
|
126
|
+
await new Promise((resolve) => setTimeout(resolve, 1e3));
|
|
127
|
+
const extensionPopup = await findExtensionPopup(context, extensionId);
|
|
128
|
+
await extensionPopup.getByRole("textbox").fill(password);
|
|
129
|
+
await extensionPopup.getByRole("button", { name: "Sign the transaction" }).click();
|
|
130
|
+
console.log("✅ Transaction signed successfully");
|
|
131
|
+
};
|
|
132
|
+
await use(approveTx);
|
|
133
|
+
}
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
const test = createWalletTest();
|
|
137
|
+
|
|
138
|
+
//#endregion
|
|
139
|
+
export { createWalletTest, downloadAndExtractPolkadotExtension, expect, test };
|
package/package.json
CHANGED
|
@@ -1,21 +1,18 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@avalix/chroma",
|
|
3
|
-
"version": "0.0.0",
|
|
4
|
-
"description": "End-to-end testing library for Polkadot wallet interactions",
|
|
5
3
|
"type": "module",
|
|
6
|
-
"
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
"
|
|
10
|
-
"
|
|
11
|
-
"
|
|
12
|
-
|
|
13
|
-
"
|
|
14
|
-
"
|
|
4
|
+
"version": "0.0.2",
|
|
5
|
+
"description": "End-to-end testing library for Polkadot wallet interactions",
|
|
6
|
+
"author": "Avalix Labs",
|
|
7
|
+
"license": "MIT",
|
|
8
|
+
"homepage": "https://github.com/avalix-labs/chroma#readme",
|
|
9
|
+
"repository": {
|
|
10
|
+
"type": "git",
|
|
11
|
+
"url": "git+https://github.com/avalix-labs/chroma.git",
|
|
12
|
+
"directory": "packages/chroma"
|
|
15
13
|
},
|
|
16
|
-
"
|
|
17
|
-
"
|
|
18
|
-
"dev": "tsdown --watch"
|
|
14
|
+
"bugs": {
|
|
15
|
+
"url": "https://github.com/avalix-labs/chroma/issues"
|
|
19
16
|
},
|
|
20
17
|
"keywords": [
|
|
21
18
|
"polkadot",
|
|
@@ -26,28 +23,35 @@
|
|
|
26
23
|
"automation",
|
|
27
24
|
"blockchain"
|
|
28
25
|
],
|
|
29
|
-
"
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
"type": "git",
|
|
33
|
-
"url": "git+https://github.com/avalix-labs/chroma.git",
|
|
34
|
-
"directory": "packages/chroma"
|
|
26
|
+
"exports": {
|
|
27
|
+
".": "./dist/index.js",
|
|
28
|
+
"./package.json": "./package.json"
|
|
35
29
|
},
|
|
36
|
-
"
|
|
37
|
-
|
|
30
|
+
"main": "./dist/index.js",
|
|
31
|
+
"module": "./dist/index.js",
|
|
32
|
+
"types": "./dist/index.d.ts",
|
|
33
|
+
"files": [
|
|
34
|
+
"dist"
|
|
35
|
+
],
|
|
36
|
+
"scripts": {
|
|
37
|
+
"build": "tsdown",
|
|
38
|
+
"dev": "tsdown --watch",
|
|
39
|
+
"lint": "eslint --fix .",
|
|
40
|
+
"prepublishOnly": "npm run build"
|
|
38
41
|
},
|
|
39
|
-
"
|
|
40
|
-
|
|
41
|
-
"@types/node": "^24.3.3",
|
|
42
|
-
"@types/unzipper": "^0.10.10",
|
|
43
|
-
"@playwright/test": "^1.55.0",
|
|
44
|
-
"tsdown": "latest"
|
|
42
|
+
"peerDependencies": {
|
|
43
|
+
"@playwright/test": "^1.55.0"
|
|
45
44
|
},
|
|
46
45
|
"dependencies": {
|
|
47
46
|
"unzipper": "^0.12.3"
|
|
48
47
|
},
|
|
49
|
-
"
|
|
50
|
-
"@
|
|
48
|
+
"devDependencies": {
|
|
49
|
+
"@antfu/eslint-config": "^5.3.0",
|
|
50
|
+
"@playwright/test": "^1.55.0",
|
|
51
|
+
"@types/node": "^24.3.3",
|
|
52
|
+
"@types/unzipper": "^0.10.10",
|
|
53
|
+
"eslint": "^9.35.0",
|
|
54
|
+
"tsdown": "latest"
|
|
51
55
|
},
|
|
52
56
|
"publishConfig": {
|
|
53
57
|
"access": "public"
|