diff --git a/packages/wasm-sdk/.gitignore b/packages/wasm-sdk/.gitignore
index 7cc3597a029..755421dbad0 100644
--- a/packages/wasm-sdk/.gitignore
+++ b/packages/wasm-sdk/.gitignore
@@ -1,3 +1,6 @@
playwright-report/
test-results/
test/test-report.html
+
+# Environment variables with sensitive test data
+test/ui-automation/.env
diff --git a/packages/wasm-sdk/api-definitions.json b/packages/wasm-sdk/api-definitions.json
index 4e959b3c527..7bc120746b9 100644
--- a/packages/wasm-sdk/api-definitions.json
+++ b/packages/wasm-sdk/api-definitions.json
@@ -2079,7 +2079,7 @@
"required": true
},
{
- "name": "identityId",
+ "name": "frozenIdentityId",
"type": "text",
"label": "Identity ID whose frozen tokens to destroy",
"required": true
diff --git a/packages/wasm-sdk/index.html b/packages/wasm-sdk/index.html
index a830f4dbadf..d01c288e0c3 100644
--- a/packages/wasm-sdk/index.html
+++ b/packages/wasm-sdk/index.html
@@ -3248,7 +3248,7 @@
Results
result = await sdk.tokenDestroyFrozen(
values.contractId,
Number(values.tokenPosition),
- values.identityId, // identity whose frozen tokens to destroy
+ values.frozenIdentityId, // identity whose frozen tokens to destroy
identityId, // destroyer ID
privateKey,
values.publicNote || null
@@ -3343,7 +3343,7 @@ Results
values.documentType,
values.documentId,
identityId,
- values.price || 0, // price in credits, 0 to remove price
+ BigInt(values.price || 0), // price in credits, 0 to remove price
privateKey,
0 // key_id - using 0 as default
);
@@ -3477,7 +3477,7 @@ Results
values.documentType,
values.documentId,
identityId,
- values.price,
+ BigInt(values.price),
privateKey,
0 // key_id - using 0 as default
);
diff --git a/packages/wasm-sdk/src/state_transitions/documents/mod.rs b/packages/wasm-sdk/src/state_transitions/documents/mod.rs
index 7bde0a1459a..8a186f2c8fa 100644
--- a/packages/wasm-sdk/src/state_transitions/documents/mod.rs
+++ b/packages/wasm-sdk/src/state_transitions/documents/mod.rs
@@ -185,6 +185,15 @@ impl WasmSdk {
Ok(result_obj.into())
}
+
+ /// Get the next revision for a document, handling errors for missing revisions and overflow
+ fn get_next_revision(document: &dash_sdk::platform::Document) -> Result {
+ let current_revision = document.revision()
+ .ok_or_else(|| JsValue::from_str("Document revision is missing"))?;
+
+ current_revision.checked_add(1)
+ .ok_or_else(|| JsValue::from_str("Document revision overflow"))
+ }
}
#[wasm_bindgen]
@@ -835,7 +844,8 @@ impl WasmSdk {
.map_err(|e| JsValue::from_str(&format!("Failed to fetch document: {}", e)))?
.ok_or_else(|| JsValue::from_str("Document not found"))?;
- let current_revision = existing_doc.revision().unwrap_or(0);
+ let current_revision = existing_doc.revision()
+ .ok_or_else(|| JsValue::from_str("Document revision is missing"))?;
// Fetch the identity to get the correct key
let identity = dash_sdk::platform::Identity::fetch(&sdk, owner_identifier)
@@ -965,6 +975,26 @@ impl WasmSdk {
.map_err(|e| JsValue::from_str(&format!("Failed to fetch document: {}", e)))?
.ok_or_else(|| JsValue::from_str("Document not found"))?;
+ // Get the current revision and increment it
+ let next_revision = Self::get_next_revision(&document)?;
+
+ // Create a modified document with incremented revision for the transfer transition
+ let transfer_document = Document::V0(DocumentV0 {
+ id: document.id(),
+ owner_id: document.owner_id(),
+ properties: document.properties().clone(),
+ revision: Some(next_revision),
+ created_at: document.created_at(),
+ updated_at: document.updated_at(),
+ transferred_at: document.transferred_at(),
+ created_at_block_height: document.created_at_block_height(),
+ updated_at_block_height: document.updated_at_block_height(),
+ transferred_at_block_height: document.transferred_at_block_height(),
+ created_at_core_block_height: document.created_at_core_block_height(),
+ updated_at_core_block_height: document.updated_at_core_block_height(),
+ transferred_at_core_block_height: document.transferred_at_core_block_height(),
+ });
+
// Fetch the identity to get the correct key
let identity = dash_sdk::platform::Identity::fetch(&sdk, owner_identifier)
.await
@@ -983,7 +1013,7 @@ impl WasmSdk {
// Create a transfer transition
let transition = BatchTransition::new_document_transfer_transition_from_document(
- document,
+ transfer_document,
document_type_ref,
recipient_identifier,
matching_key,
@@ -1089,6 +1119,26 @@ impl WasmSdk {
)));
}
+ // Get the current revision and increment it
+ let next_revision = Self::get_next_revision(&document)?;
+
+ // Create a modified document with incremented revision for the purchase transition
+ let purchase_document = Document::V0(DocumentV0 {
+ id: document.id(),
+ owner_id: document.owner_id(),
+ properties: document.properties().clone(),
+ revision: Some(next_revision),
+ created_at: document.created_at(),
+ updated_at: document.updated_at(),
+ transferred_at: document.transferred_at(),
+ created_at_block_height: document.created_at_block_height(),
+ updated_at_block_height: document.updated_at_block_height(),
+ transferred_at_block_height: document.transferred_at_block_height(),
+ created_at_core_block_height: document.created_at_core_block_height(),
+ updated_at_core_block_height: document.updated_at_core_block_height(),
+ transferred_at_core_block_height: document.transferred_at_core_block_height(),
+ });
+
// Fetch buyer identity
let buyer_identity = dash_sdk::platform::Identity::fetch(&sdk, buyer_identifier)
.await
@@ -1107,7 +1157,7 @@ impl WasmSdk {
// Create document purchase transition
let transition = BatchTransition::new_document_purchase_transition_from_document(
- document.into(),
+ purchase_document,
document_type_ref,
buyer_identifier,
price as Credits,
@@ -1226,25 +1276,15 @@ impl WasmSdk {
return Err(JsValue::from_str("Only the document owner can set its price"));
}
- // Get existing document properties and convert to mutable map
- let mut properties = existing_doc.properties().clone();
-
- // Update the price in the document properties
- let price_value = if price > 0 {
- PlatformValue::U64(price)
- } else {
- PlatformValue::Null
- };
+ // Get the current revision and increment it
+ let next_revision = Self::get_next_revision(&existing_doc)?;
- properties.insert("$price".to_string(), price_value);
-
- // Create updated document with new properties
- let new_revision = existing_doc.revision().unwrap_or(0) + 1;
- let updated_doc = Document::V0(DocumentV0 {
- id: doc_id,
- owner_id: owner_identifier,
- properties,
- revision: Some(new_revision),
+ // Create a modified document with incremented revision for the price update transition
+ let price_update_document = Document::V0(DocumentV0 {
+ id: existing_doc.id(),
+ owner_id: existing_doc.owner_id(),
+ properties: existing_doc.properties().clone(),
+ revision: Some(next_revision),
created_at: existing_doc.created_at(),
updated_at: existing_doc.updated_at(),
transferred_at: existing_doc.transferred_at(),
@@ -1272,22 +1312,12 @@ impl WasmSdk {
.await
.map_err(|e| JsValue::from_str(&format!("Failed to fetch nonce: {}", e)))?;
- // Generate entropy for the state transition
- let entropy_bytes = {
- let mut entropy = [0u8; 32];
- if let Some(window) = web_sys::window() {
- if let Ok(crypto) = window.crypto() {
- let _ = crypto.get_random_values_with_u8_array(&mut entropy);
- }
- }
- entropy
- };
-
- // Create the price update transition
- let transition = BatchTransition::new_document_replacement_transition_from_document(
- updated_doc,
+ // Create the price update transition using the dedicated method
+ let transition = BatchTransition::new_document_update_price_transition_from_document(
+ price_update_document,
document_type_ref,
- matching_key,
+ price,
+ &matching_key,
identity_contract_nonce,
UserFeeIncrease::default(),
None, // token_payment_info
@@ -1295,7 +1325,7 @@ impl WasmSdk {
sdk.version(),
None, // options
)
- .map_err(|e| JsValue::from_str(&format!("Failed to create transition: {}", e)))?;
+ .map_err(|e| JsValue::from_str(&format!("Failed to create price update transition: {}", e)))?;
// The transition is already signed, convert to StateTransition
let state_transition: StateTransition = transition.into();
diff --git a/packages/wasm-sdk/test/ui-automation/.env.example b/packages/wasm-sdk/test/ui-automation/.env.example
new file mode 100644
index 00000000000..ee0b0a1ea4e
--- /dev/null
+++ b/packages/wasm-sdk/test/ui-automation/.env.example
@@ -0,0 +1,12 @@
+# Test Credentials for WASM SDK UI Tests
+# Copy this file to .env and fill with real values
+
+# Private keys for state transitions (DON'T STORE in production)
+TEST_PRIVATE_KEY_IDENTITY_1=YOUR_IDENTITY_PRIVATE_KEY_HERE
+TEST_PRIVATE_KEY_TRANSFER=YOUR_TRANSFER_PRIVATE_KEY_HERE
+TEST_PRIVATE_KEY_CONTRACT=YOUR_CONTRACT_PRIVATE_KEY_HERE
+# Secondary private key (used by some document/token transitions)
+TEST_PRIVATE_KEY_SECONDARY=YOUR_TEST_PRIVATE_KEY_SECONDARY
+
+# Seed phrases for identity creation (not implemented yet)
+TEST_SEED_PHRASE_1="your seed phrase here"
diff --git a/packages/wasm-sdk/test/ui-automation/fixtures/test-data.js b/packages/wasm-sdk/test/ui-automation/fixtures/test-data.js
index f94999c18ad..b1b731e1e43 100644
--- a/packages/wasm-sdk/test/ui-automation/fixtures/test-data.js
+++ b/packages/wasm-sdk/test/ui-automation/fixtures/test-data.js
@@ -3,6 +3,9 @@
* Based on update_inputs.py and existing test files
*/
+// Load environment variables for sensitive test data
+require('dotenv').config({ path: require('path').join(__dirname, '../.env') });
+
const testData = {
// Known testnet identity IDs for testing (from WASM SDK docs and tests)
identityIds: {
@@ -109,7 +112,7 @@ const testData = {
],
contractId: "GWRSAVFMjXx8HpQFaNJMqBV7MBgMK4br5UESsB4S31Ec",
documentTypeName: "domain",
- keyRequestType: "all"
+ purposes: ["0", "3"] // Authentication and Transfer
}
]
},
@@ -493,6 +496,302 @@ const testData = {
}
},
+ // State transition test parameters organized by category
+ stateTransitionParameters: {
+ identity: {
+ identityCreate: {
+ testnet: [
+ {
+ seedPhrase: process.env.TEST_SEED_PHRASE_1 || "placeholder seed phrase",
+ identityIndex: 0,
+ keySelectionMode: "simple",
+ assetLockProof: "a914b7e904ce25ed97594e72f7af0e66f298031c175487",
+ privateKey: process.env.TEST_PRIVATE_KEY_IDENTITY_1 || "PLACEHOLDER_IDENTITY_KEY_1",
+ expectedKeys: 2,
+ description: "Test identity creation with standard seed phrase"
+ }
+ ]
+ },
+ identityTopUp: {
+ testnet: [
+ {
+ identityId: "5DbLwAxGBzUzo81VewMUwn4b5P4bpv9FNFybi25XB5Bk",
+ assetLockProof: "a914b7e904ce25ed97594e72f7af0e66f298031c175487",
+ privateKey: process.env.TEST_PRIVATE_KEY_IDENTITY_1 || "PLACEHOLDER_IDENTITY_KEY_1",
+ description: "Top up existing identity with credits"
+ }
+ ]
+ },
+ identityCreditTransfer: {
+ testnet: [
+ {
+ identityId: "7XcruVSsGQVSgTcmPewaE4tXLutnW1F6PXxwMbo8GYQC",
+ recipientId: "HJDxtN6FJF3U3T9TMLWCqudfJ5VRkaUrxTsRp36djXAG",
+ amount: 100000, // 0.000001 DASH in credits
+ privateKey: process.env.TEST_PRIVATE_KEY_TRANSFER || "PLACEHOLDER_TRANSFER_KEY", // Transfer key
+ description: "Transfer credits between identities"
+ }
+ ]
+ },
+ identityCreditWithdrawal: {
+ testnet: [
+ {
+ identityId: "7XcruVSsGQVSgTcmPewaE4tXLutnW1F6PXxwMbo8GYQC",
+ toAddress: "yQW6TmUFef5CDyhEYwjoN8aUTMmKLYYNDm",
+ amount: 190000, // 0.0000019 DASH in credits (minimum withdrawal amount)
+ privateKey: process.env.TEST_PRIVATE_KEY_TRANSFER || "PLACEHOLDER_TRANSFER_KEY",
+ description: "Withdraw credits to Dash address"
+ }
+ ]
+ }
+ },
+ dataContract: {
+ dataContractCreate: {
+ testnet: [
+ {
+ canBeDeleted: false,
+ readonly: false,
+ keepsHistory: false,
+ documentSchemas: '{"note": {"type": "object", "properties": {"message": {"type": "string", "position": 0}}, "additionalProperties": false}}',
+ identityId: "7XcruVSsGQVSgTcmPewaE4tXLutnW1F6PXxwMbo8GYQC",
+ privateKey: process.env.TEST_PRIVATE_KEY_CONTRACT || "PLACEHOLDER_CONTRACT_KEY",
+ description: "Create simple test data contract with document schema"
+ }
+ ]
+ },
+ dataContractUpdate: {
+ testnet: [
+ {
+ dataContractId: "5kMgvQ9foEQ9TzDhz5jvbJ9Lhv5qqBpUeYEezHNEa6Ti", // Sample contract ID
+ newDocumentSchemas: '{"note": {"type": "object", "properties": {"message": {"type": "string", "position": 0}, "author": {"type": "string", "position": 1}}, "additionalProperties": false}}',
+ identityId: "7XcruVSsGQVSgTcmPewaE4tXLutnW1F6PXxwMbo8GYQC",
+ privateKey: process.env.TEST_PRIVATE_KEY_CONTRACT || "PLACEHOLDER_CONTRACT_KEY",
+ description: "Update existing note document schema to add author field"
+ }
+ ]
+ }
+ },
+ document: {
+ documentCreate: {
+ testnet: [
+ {
+ contractId: "5kMgvQ9foEQ9TzDhz5jvbJ9Lhv5qqBpUeYEezHNEa6Ti", // Use simple note contract (will be created by dataContractCreate test)
+ documentType: "note",
+ documentFields: {
+ message: "Document created for WASM-SDK UI testing"
+ },
+ identityId: "7XcruVSsGQVSgTcmPewaE4tXLutnW1F6PXxwMbo8GYQC",
+ privateKey: process.env.TEST_PRIVATE_KEY_CONTRACT || "PLACEHOLDER_CONTRACT_KEY",
+ description: "Create test note document with simple schema"
+ }
+ ]
+ },
+ documentReplace: {
+ testnet: [
+ {
+ contractId: "5kMgvQ9foEQ9TzDhz5jvbJ9Lhv5qqBpUeYEezHNEa6Ti", // Use simple note contract
+ documentType: "note",
+ documentId: "Dy19ZeYPpqbEDcpsPcLwkviY5GZqT7yJL2EY4YfxTYjn", // Persistent testnet document
+ documentFields: {
+ message: "Updated document message for automation testing"
+ },
+ identityId: "7XcruVSsGQVSgTcmPewaE4tXLutnW1F6PXxwMbo8GYQC",
+ privateKey: process.env.TEST_PRIVATE_KEY_CONTRACT || "PLACEHOLDER_CONTRACT_KEY",
+ description: "Replace existing note document"
+ }
+ ]
+ },
+ documentDelete: {
+ testnet: [
+ {
+ contractId: "5kMgvQ9foEQ9TzDhz5jvbJ9Lhv5qqBpUeYEezHNEa6Ti", // Use simple note contract
+ documentType: "note",
+ documentId: "PLACEHOLDER_DOCUMENT_ID", // Will be set dynamically
+ identityId: "7XcruVSsGQVSgTcmPewaE4tXLutnW1F6PXxwMbo8GYQC",
+ privateKey: process.env.TEST_PRIVATE_KEY_CONTRACT || "PLACEHOLDER_CONTRACT_KEY",
+ description: "Delete existing note document"
+ }
+ ]
+ },
+ documentTransfer: {
+ testnet: [
+ {
+ identityId: "HJDxtN6FJF3U3T9TMLWCqudfJ5VRkaUrxTsRp36djXAG", // Current owner
+ privateKey: process.env.TEST_PRIVATE_KEY_SECONDARY || "PLACEHOLDER_CONTRACT_KEY",
+ contractId: "HdRFTcxgwPSVgzdy6MTYutDLJdbpfLMXwuBaYLYKMVHv", // Use NFT contract
+ documentType: "card",
+ documentId: "EypPkQLgT6Jijht7NYs4jmK5TGzkNd1Z4WrQdH1hND59", // Existing trading card document
+ recipientId: "7XcruVSsGQVSgTcmPewaE4tXLutnW1F6PXxwMbo8GYQC", // Transfer recipient
+ description: "Transfer trading card ownership to secondary identity"
+ }
+ ]
+ },
+ documentPurchase: {
+ testnet: [
+ {
+ identityId: "HJDxtN6FJF3U3T9TMLWCqudfJ5VRkaUrxTsRp36djXAG", // Buyer identity
+ privateKey: process.env.TEST_PRIVATE_KEY_SECONDARY || "PLACEHOLDER_SECONDARY_KEY",
+ contractId: "HdRFTcxgwPSVgzdy6MTYutDLJdbpfLMXwuBaYLYKMVHv", // Use NFT contract
+ documentType: "card",
+ documentId: "EypPkQLgT6Jijht7NYs4jmK5TGzkNd1Z4WrQdH1hND59", // Existing trading card document
+ price: 1000, // Price in credits
+ description: "Purchase a priced trading card with secondary identity"
+ }
+ ]
+ },
+ documentSetPrice: {
+ testnet: [
+ {
+ identityId: "7XcruVSsGQVSgTcmPewaE4tXLutnW1F6PXxwMbo8GYQC", // Primary identity owns card after creation
+ privateKey: process.env.TEST_PRIVATE_KEY_CONTRACT || "PLACEHOLDER_CONTRACT_KEY",
+ contractId: "HdRFTcxgwPSVgzdy6MTYutDLJdbpfLMXwuBaYLYKMVHv", // Use NFT contract
+ documentType: "card",
+ documentId: "EypPkQLgT6Jijht7NYs4jmK5TGzkNd1Z4WrQdH1hND59", // Existing trading card document
+ price: 1000, // Price in credits
+ description: "Set price for a trading card"
+ }
+ ]
+ }
+ },
+ token: {
+ tokenMint: {
+ testnet: [
+ {
+ contractId: "Afk9QSj9UDE14K1y9y3iSx6kUSm5LLmhbdAvPvWL4P2i",
+ tokenPosition: 0,
+ amount: "2",
+ identityId: "7XcruVSsGQVSgTcmPewaE4tXLutnW1F6PXxwMbo8GYQC",
+ privateKey: process.env.TEST_PRIVATE_KEY_CONTRACT || "PLACEHOLDER_CONTRACT_KEY",
+ // issuedToIdentityId: "7XcruVSsGQVSgTcmPewaE4tXLutnW1F6PXxwMbo8GYQC",
+ publicNote: "Token mint test",
+ description: "Mint new tokens (may fail without permissions)"
+ }
+ ]
+ },
+ tokenTransfer: {
+ testnet: [
+ {
+ contractId: "Afk9QSj9UDE14K1y9y3iSx6kUSm5LLmhbdAvPvWL4P2i",
+ tokenPosition: 0,
+ amount: "1",
+ identityId: "7XcruVSsGQVSgTcmPewaE4tXLutnW1F6PXxwMbo8GYQC",
+ privateKey: process.env.TEST_PRIVATE_KEY_CONTRACT || "PLACEHOLDER_CONTRACT_KEY",
+ recipientId: "HJDxtN6FJF3U3T9TMLWCqudfJ5VRkaUrxTsRp36djXAG",
+ publicNote: "Token transfer test",
+ description: "Transfer tokens between identities"
+ }
+ ]
+ },
+ tokenBurn: {
+ testnet: [
+ {
+ contractId: "Afk9QSj9UDE14K1y9y3iSx6kUSm5LLmhbdAvPvWL4P2i",
+ tokenPosition: 0,
+ amount: "1",
+ identityId: "7XcruVSsGQVSgTcmPewaE4tXLutnW1F6PXxwMbo8GYQC",
+ privateKey: process.env.TEST_PRIVATE_KEY_CONTRACT || "PLACEHOLDER_CONTRACT_KEY",
+ publicNote: "Token burn test",
+ description: "Burn tokens from identity balance"
+ }
+ ]
+ },
+ tokenFreeze: {
+ testnet: [
+ {
+ contractId: "Afk9QSj9UDE14K1y9y3iSx6kUSm5LLmhbdAvPvWL4P2i",
+ tokenPosition: 0,
+ identityId: "7XcruVSsGQVSgTcmPewaE4tXLutnW1F6PXxwMbo8GYQC",
+ identityToFreeze: "HJDxtN6FJF3U3T9TMLWCqudfJ5VRkaUrxTsRp36djXAG",
+ privateKey: process.env.TEST_PRIVATE_KEY_CONTRACT || "PLACEHOLDER_CONTRACT_KEY",
+ publicNote: "Token freeze test",
+ description: "Freeze tokens for an identity"
+ }
+ ]
+ },
+ tokenDestroyFrozen: {
+ testnet: [
+ {
+ contractId: "Afk9QSj9UDE14K1y9y3iSx6kUSm5LLmhbdAvPvWL4P2i",
+ tokenPosition: 0,
+ identityId: "7XcruVSsGQVSgTcmPewaE4tXLutnW1F6PXxwMbo8GYQC",
+ frozenIdentityId: "HJDxtN6FJF3U3T9TMLWCqudfJ5VRkaUrxTsRp36djXAG",
+ privateKey: process.env.TEST_PRIVATE_KEY_CONTRACT || "PLACEHOLDER_CONTRACT_KEY",
+ publicNote: "Destroy frozen tokens test",
+ description: "Destroy frozen tokens from an identity"
+ }
+ ]
+ },
+ tokenUnfreeze: {
+ testnet: [
+ {
+ contractId: "Afk9QSj9UDE14K1y9y3iSx6kUSm5LLmhbdAvPvWL4P2i",
+ tokenPosition: 0,
+ identityId: "7XcruVSsGQVSgTcmPewaE4tXLutnW1F6PXxwMbo8GYQC",
+ identityToUnfreeze: "HJDxtN6FJF3U3T9TMLWCqudfJ5VRkaUrxTsRp36djXAG",
+ privateKey: process.env.TEST_PRIVATE_KEY_CONTRACT || "PLACEHOLDER_CONTRACT_KEY",
+ publicNote: "Token unfreeze test",
+ description: "Unfreeze tokens for an identity"
+ }
+ ]
+ },
+ tokenClaim: {
+ testnet: [
+ {
+ identityId: "7XcruVSsGQVSgTcmPewaE4tXLutnW1F6PXxwMbo8GYQC",
+ privateKey: process.env.TEST_PRIVATE_KEY_CONTRACT || "PLACEHOLDER_CONTRACT_KEY",
+ contractId: "Afk9QSj9UDE14K1y9y3iSx6kUSm5LLmhbdAvPvWL4P2i",
+ tokenPosition: 0,
+ distributionType: "perpetual",
+ publicNote: "Token claim test",
+ description: "Claim tokens from distribution"
+ }
+ ]
+ },
+ tokenSetPriceForDirectPurchase: {
+ testnet: [
+ {
+ identityId: "7XcruVSsGQVSgTcmPewaE4tXLutnW1F6PXxwMbo8GYQC",
+ privateKey: process.env.TEST_PRIVATE_KEY_CONTRACT || "PLACEHOLDER_CONTRACT_KEY",
+ contractId: "Afk9QSj9UDE14K1y9y3iSx6kUSm5LLmhbdAvPvWL4P2i",
+ tokenPosition: 0,
+ priceType: "single",
+ priceData: "10",
+ publicNote: "Set token price test",
+ description: "Set price for direct token purchases"
+ }
+ ]
+ },
+ tokenDirectPurchase: {
+ testnet: [
+ {
+ identityId: "7XcruVSsGQVSgTcmPewaE4tXLutnW1F6PXxwMbo8GYQC",
+ privateKey: process.env.TEST_PRIVATE_KEY_CONTRACT || "PLACEHOLDER_CONTRACT_KEY",
+ contractId: "Afk9QSj9UDE14K1y9y3iSx6kUSm5LLmhbdAvPvWL4P2i",
+ tokenPosition: 0,
+ amount: "1",
+ totalAgreedPrice: "10",
+ keyId: 0,
+ description: "Direct purchase of tokens at configured price"
+ }
+ ]
+ },
+ tokenConfigUpdate: {
+ testnet: [
+ {
+ identityId: "7XcruVSsGQVSgTcmPewaE4tXLutnW1F6PXxwMbo8GYQC",
+ privateKey: process.env.TEST_PRIVATE_KEY_CONTRACT || "PLACEHOLDER_CONTRACT_KEY",
+ contractId: "Afk9QSj9UDE14K1y9y3iSx6kUSm5LLmhbdAvPvWL4P2i",
+ tokenPosition: 0,
+ configItemType: "max_supply",
+ configValue: "1000000",
+ publicNote: "Update max supply test",
+ description: "Update token configuration max supply"
+ }
+ ]
+ },
+ }
+ },
+
// Common where clauses for document queries
whereClausesExamples: {
dpnsDomain: [
@@ -543,8 +842,45 @@ function getAllTestParameters(category, queryType, network = 'testnet') {
return queryData[network] || [];
}
+/**
+ * Get test parameters for a specific state transition
+ */
+function getStateTransitionParameters(category, transitionType, network = 'testnet') {
+ const categoryData = testData.stateTransitionParameters[category];
+ if (!categoryData) {
+ throw new Error(`No state transition test data found for category: ${category}`);
+ }
+
+ const transitionData = categoryData[transitionType];
+ if (!transitionData) {
+ throw new Error(`No state transition test data found for transition: ${category}.${transitionType}`);
+ }
+
+ const networkData = transitionData[network];
+ if (!networkData || networkData.length === 0) {
+ throw new Error(`No state transition test data found for ${category}.${transitionType} on ${network}`);
+ }
+
+ return networkData[0]; // Return first test case
+}
+
+/**
+ * Get all test parameters for a state transition (for parameterized testing)
+ */
+function getAllStateTransitionParameters(category, transitionType, network = 'testnet') {
+ const categoryData = testData.stateTransitionParameters[category];
+ if (!categoryData) return [];
+
+ const transitionData = categoryData[transitionType];
+ if (!transitionData) return [];
+
+ return transitionData[network] || [];
+}
+
module.exports = {
testData,
getTestParameters,
- getAllTestParameters
+ getAllTestParameters,
+ getStateTransitionParameters,
+ getAllStateTransitionParameters
};
diff --git a/packages/wasm-sdk/test/ui-automation/package-lock.json b/packages/wasm-sdk/test/ui-automation/package-lock.json
index 7c1d8959081..86e0d8971d3 100644
--- a/packages/wasm-sdk/test/ui-automation/package-lock.json
+++ b/packages/wasm-sdk/test/ui-automation/package-lock.json
@@ -7,8 +7,11 @@
"": {
"name": "wasm-sdk-ui-automation",
"version": "1.0.0",
+ "dependencies": {
+ "dotenv": "^17.2.1"
+ },
"devDependencies": {
- "@playwright/test": "^1.41.0"
+ "@playwright/test": "^1.54.1"
},
"engines": {
"node": ">=18"
@@ -30,6 +33,18 @@
"node": ">=18"
}
},
+ "node_modules/dotenv": {
+ "version": "17.2.1",
+ "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.2.1.tgz",
+ "integrity": "sha512-kQhDYKZecqnM0fCnzI5eIv5L4cAe/iRI+HqMbO/hbRdTAeXDG+M9FjipUxNfbARuEg4iHIbhnhs78BCHNbSxEQ==",
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://dotenvx.com"
+ }
+ },
"node_modules/fsevents": {
"version": "2.3.2",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
diff --git a/packages/wasm-sdk/test/ui-automation/package.json b/packages/wasm-sdk/test/ui-automation/package.json
index 8785b6257c4..78bcefca2f5 100644
--- a/packages/wasm-sdk/test/ui-automation/package.json
+++ b/packages/wasm-sdk/test/ui-automation/package.json
@@ -24,5 +24,8 @@
},
"engines": {
"node": ">=18"
+ },
+ "dependencies": {
+ "dotenv": "^17.2.1"
}
}
diff --git a/packages/wasm-sdk/test/ui-automation/playwright.config.js b/packages/wasm-sdk/test/ui-automation/playwright.config.js
index 4e8c47a7400..5119dde4d90 100644
--- a/packages/wasm-sdk/test/ui-automation/playwright.config.js
+++ b/packages/wasm-sdk/test/ui-automation/playwright.config.js
@@ -6,14 +6,10 @@ const { defineConfig, devices } = require('@playwright/test');
*/
module.exports = defineConfig({
testDir: './tests',
- /* Run tests in files in parallel */
- fullyParallel: true,
/* Fail the build on CI if you accidentally left test.only in the source code. */
forbidOnly: !!process.env.CI,
/* Retry on CI only */
retries: process.env.CI ? 2 : 0,
- /* Opt out of parallel tests on CI. */
- workers: process.env.CI ? 1 : undefined,
/* Reporter to use. See https://playwright.dev/docs/test-reporters */
reporter: [
['html', { outputFolder: 'playwright-report' }],
@@ -41,10 +37,13 @@ module.exports = defineConfig({
navigationTimeout: 30000,
},
- /* Configure projects for major browsers */
+ /* Configure projects for different test execution modes */
projects: [
{
- name: 'chromium',
+ name: 'parallel-tests',
+ testMatch: ['basic-smoke.spec.js', 'query-execution.spec.js', 'parameterized-queries.spec.js'],
+ fullyParallel: true,
+ workers: process.env.CI ? 1 : undefined,
use: {
...devices['Desktop Chrome'],
// Enable headless mode by default
@@ -53,6 +52,21 @@ module.exports = defineConfig({
viewport: { width: 1920, height: 1080 }
},
},
+ // Skip state transitions tests in CI environments
+ // These are very slow-running due to https://github.com/dashpay/platform/issues/2736
+ ...(process.env.CI ? [] : [{
+ name: 'sequential-tests',
+ testMatch: ['state-transitions.spec.js'],
+ fullyParallel: false, // Tests in file run in order
+ workers: 1, // Single worker for sequential execution
+ use: {
+ ...devices['Desktop Chrome'],
+ // Enable headless mode by default
+ headless: true,
+ // Use a larger viewport for better testing
+ viewport: { width: 1920, height: 1080 }
+ },
+ }]),
],
/* Run your local dev server before starting the tests */
diff --git a/packages/wasm-sdk/test/ui-automation/tests/query-execution.spec.js b/packages/wasm-sdk/test/ui-automation/tests/query-execution.spec.js
index cfa16105560..b375d8f5dde 100644
--- a/packages/wasm-sdk/test/ui-automation/tests/query-execution.spec.js
+++ b/packages/wasm-sdk/test/ui-automation/tests/query-execution.spec.js
@@ -195,6 +195,27 @@ function validateKeysResult(resultStr) {
});
}
+function validateIdentitiesContractKeysResult(resultStr) {
+ expect(() => JSON.parse(resultStr)).not.toThrow();
+ const contractKeysData = JSON.parse(resultStr);
+ expect(contractKeysData).toBeDefined();
+ expect(Array.isArray(contractKeysData)).toBe(true);
+
+ contractKeysData.forEach(identityResult => {
+ expect(identityResult).toHaveProperty('identityId');
+ expect(identityResult).toHaveProperty('keys');
+ expect(Array.isArray(identityResult.keys)).toBe(true);
+
+ identityResult.keys.forEach(key => {
+ expect(key).toHaveProperty('keyId');
+ expect(key).toHaveProperty('purpose');
+ expect(key).toHaveProperty('keyType');
+ expect(key).toHaveProperty('publicKeyData');
+ expect(key).toHaveProperty('securityLevel');
+ });
+ });
+}
+
function validateIdentitiesResult(resultStr) {
expect(() => JSON.parse(resultStr)).not.toThrow();
const identitiesData = JSON.parse(resultStr);
@@ -1270,7 +1291,7 @@ test.describe('WASM SDK Query Execution Tests', () => {
{ name: 'getIdentityNonce', hasProofSupport: true, validateFn: (result) => validateNumericResult(result, 'nonce') },
{ name: 'getIdentityContractNonce', hasProofSupport: true, validateFn: (result) => validateNumericResult(result, 'nonce') },
{ name: 'getIdentityByPublicKeyHash', hasProofSupport: true, validateFn: validateIdentityResult },
- { name: 'getIdentitiesContractKeys', hasProofSupport: true, validateFn: validateKeysResult },
+ { name: 'getIdentitiesContractKeys', hasProofSupport: true, validateFn: validateIdentitiesContractKeysResult },
{ name: 'getIdentitiesBalances', hasProofSupport: true, validateFn: validateBalancesResult },
{ name: 'getIdentityBalanceAndRevision', hasProofSupport: true, validateFn: validateBalanceAndRevisionResult },
{ name: 'getIdentityByNonUniquePublicKeyHash', hasProofSupport: true, validateFn: validateIdentitiesResult },
diff --git a/packages/wasm-sdk/test/ui-automation/tests/state-transitions.spec.js b/packages/wasm-sdk/test/ui-automation/tests/state-transitions.spec.js
new file mode 100644
index 00000000000..f0f80a0841a
--- /dev/null
+++ b/packages/wasm-sdk/test/ui-automation/tests/state-transitions.spec.js
@@ -0,0 +1,1474 @@
+const { test, expect } = require('@playwright/test');
+const { WasmSdkPage } = require('../utils/wasm-sdk-page');
+const { ParameterInjector } = require('../utils/parameter-injector');
+
+/**
+ * Helper function to execute a state transition
+ * @param {WasmSdkPage} wasmSdkPage - The page object instance
+ * @param {ParameterInjector} parameterInjector - The parameter injector instance
+ * @param {string} category - State transition category (e.g., 'identity', 'dataContract')
+ * @param {string} transitionType - Transition type (e.g., 'identityCreate')
+ * @param {string} network - Network to use ('testnet' or 'mainnet')
+ * @returns {Promise