OverviewFilesCommitsIssuesDocsReleasesPeers

hyper-wallet-desktop / test / helpers.js

140 lines · javascript ref:
// test/helpers.js - Shared Test Utilities

const crypto = require('hypercore-crypto')
const b4a = require('b4a')

/**
 * Generate random hex token for testing
 * @param {number} bytes - Number of bytes (default: 32)
 * @returns {string} - Hex string
 */
function generateTestToken(bytes = 32) {
  return b4a.toString(crypto.randomBytes(bytes), 'hex')
}

/**
 * Generate test mnemonic (NOT cryptographically secure - testing only!)
 * @param {number} words - Number of words (12 or 24)
 * @returns {string} - Space-separated mnemonic
 */
function generateTestMnemonic(words = 12) {
  // Use deterministic test mnemonics from fixtures
  const testWords = [
    'abandon', 'ability', 'able', 'about', 'above', 'absent',
    'absorb', 'abstract', 'absurd', 'abuse', 'access', 'accident',
    'account', 'accuse', 'achieve', 'acid', 'acoustic', 'acquire',
    'across', 'act', 'action', 'actor', 'actress', 'actual'
  ]

  return testWords.slice(0, words).join(' ')
}

/**
 * Wait for async condition to be true
 * @param {Function} condition - Async function returning boolean
 * @param {number} timeout - Max wait time in ms (default: 5000)
 * @param {number} interval - Check interval in ms (default: 100)
 * @returns {Promise<void>}
 */
async function waitFor(condition, timeout = 5000, interval = 100) {
  const startTime = Date.now()

  while (Date.now() - startTime < timeout) {
    if (await condition()) {
      return
    }
    await new Promise(resolve => setTimeout(resolve, interval))
  }

  throw new Error(`Timeout waiting for condition after ${timeout}ms`)
}

/**
 * Assert function throws with expected error
 * @param {Function} fn - Function that should throw
 * @param {string|RegExp} expectedError - Expected error message or pattern
 * @returns {Promise<void>}
 */
async function assertThrows(fn, expectedError) {
  let threw = false
  let error = null

  try {
    await fn()
  } catch (err) {
    threw = true
    error = err
  }

  if (!threw) {
    throw new Error('Expected function to throw but it did not')
  }

  if (expectedError instanceof RegExp) {
    if (!expectedError.test(error.message)) {
      throw new Error(`Expected error matching ${expectedError} but got: ${error.message}`)
    }
  } else if (typeof expectedError === 'string') {
    if (!error.message.includes(expectedError)) {
      throw new Error(`Expected error containing "${expectedError}" but got: ${error.message}`)
    }
  }
}

/**
 * Create temporary test directory
 * @returns {string} - Path to temp directory
 */
function createTempDir() {
  const os = require('bare-os')
  const path = require('bare-path')
  const fs = require('bare-fs')

  const tmpDir = path.join(os.tmpdir(), `hyper-wallet-test-${Date.now()}`)
  fs.mkdirSync(tmpDir, { recursive: true, mode: 0o700 })
  return tmpDir
}

/**
 * Clean up temporary test directory
 * @param {string} dir - Directory to remove
 */
function cleanupTempDir(dir) {
  const fs = require('bare-fs')
  const path = require('bare-path')

  if (fs.existsSync(dir)) {
    const files = fs.readdirSync(dir)
    for (const file of files) {
      const filePath = path.join(dir, file)
      const stat = fs.statSync(filePath)

      if (stat.isDirectory()) {
        cleanupTempDir(filePath)
      } else {
        fs.unlinkSync(filePath)
      }
    }
    fs.rmdirSync(dir)
  }
}

/**
 * Sleep for specified milliseconds
 * @param {number} ms - Milliseconds to sleep
 * @returns {Promise<void>}
 */
function sleep(ms) {
  return new Promise(resolve => setTimeout(resolve, ms))
}

module.exports = {
  generateTestToken,
  generateTestMnemonic,
  waitFor,
  assertThrows,
  createTempDir,
  cleanupTempDir,
  sleep
}