Skip to content

Use-Tusk/tusk-drift-animation

Repository files navigation

Tusk API Testing Animation

An animated GIF that explains how Tusk's API testing product works by visualizing the Record and Replay modes of the Tusk SDK.

Overview

This project creates an animated explanation of Tusk Drift's API testing mechanism, which works by:

  1. Record Mode: Intercepting outbound API calls (SQL queries, HTTP requests, etc.) at the service boundary and storing them in Tusk Cloud
  2. Replay Mode: Running tests against a local service where the SDK replays cached responses instead of making real external calls

The animation helps developers understand how Tusk achieves fast, deterministic, and isolated API tests.


What We're Creating

Visual Components

  • Client / Tusk CLI: Left side box (dark gray for Client, purple for CLI)
  • Service Under Test: Center gray box with purple dashed SDK border
  • Database: Right side blue cylinder (active in Record, grayed out in Replay)
  • Tusk Cloud: Bottom center purple cloud icon
  • Animated Arrows: Show request/response flow with labels

The Two Modes

RECORD Mode (7 seconds)

Shows how the SDK captures API calls during normal operation:

Relevant Shapes:

  • Client (left side, dark gray box)
  • Service Under Test (center, gray box with black border)
  • Tusk SDK (dashed purple border around Service)
  • Database (right side, blue cylinder - full color, active)
  • Tusk Cloud (bottom center, purple cloud icon)

Order of Events:

  1. Client → Service (0.8s)

    • Arrow animates from Client to Service
    • Label: GET /api/post/1
    • Data passed: HTTP request with endpoint and parameters
  2. Service processes (0.3s)

    • Service box glows briefly
    • Service receives request and prepares database query
  3. Service → Database (0.7s)

    • Arrow animates from Service to Database
    • Label: SELECT * FROM posts WHERE id = 1
    • Data passed: SQL query with parameters
  4. Database processes (0.4s)

    • Database cylinder pulses/glows (showing it's active)
    • Query executes against real database
  5. Database → Service (0.7s)

    • Arrow animates back from Database to Service
    • Label: { id: "1", title: "Post title" }
    • Data passed: Database result set with post data
  6. SDK captures (0.4s)

    • Purple dashed border pulses (passive observation)
    • SDK intercepts and records the DB call and response
    • Note: SDK doesn't block - it observes the real call happening
  7. Service → Client (0.7s)

    • Arrow animates from Service to Client
    • Label: { id: "1", title: "Post title" }
    • Data passed: HTTP response with post data
  8. SDK → Tusk Cloud (0.9s)

    • Arrow animates from SDK border to Tusk Cloud
    • Label: Export spans
    • Data passed: Recorded span data (request + response + timing)
  9. Tusk Cloud stores (0.5s)

    • Cloud scales/glows to indicate receipt
    • Indicator: Spans recorded ✓
    • Span data saved for future replay
  10. Pause (1.5s): Before transitioning to Replay mode

Key Visual: Database is active and full color, SDK observes passively without blocking

REPLAY Mode (6 seconds)

Shows how tests run without hitting real dependencies - database is bypassed entirely:

Relevant Shapes:

  • Tusk CLI (left side, purple box - test orchestrator)
  • Service Under Test (center, gray box with black border)
  • Tusk SDK (dashed purple border around Service)
  • Database (right side, grayed out cylinder with ✕ mark - NOT called)
  • Tusk Cloud (bottom center, purple cloud icon)

Order of Events:

  1. Tusk Cloud → Tusk CLI (0.8s)

    • Arrow animates from Cloud to CLI
    • Label: Fetch test spans
    • Data passed: Previously recorded span data downloaded before test runs
    • Note: This happens first - CLI pre-loads the test data
  2. Tusk CLI receives (0.3s)

    • CLI box pulses briefly
    • Test runner now has all recorded spans in memory
  3. Tusk CLI → Service (0.8s)

    • Arrow animates from CLI to Service
    • Label: GET /api/post/1
    • Data passed: Same HTTP request as Record mode
  4. Service processes (0.3s)

    • Service box glows briefly
    • Service receives request and attempts to query database
  5. Service attempts Database call (0.4s)

    • Faded/dotted arrow starts animating toward Database
    • Label: SELECT * FROM posts WHERE id = 1
    • Arrow stops mid-way (intercepted before reaching DB)
    • Data passed: Query intent, but never reaches database
  6. SDK intercepts (0.5s)

    • Purple dashed border strongly pulses/glows (active blocking)
    • Arrow stops completely before reaching Database
    • Label: Found span appears between Service and Database
    • Note: SDK actively blocks the call - this is the key difference from Record mode
  7. Database bypassed (0.3s)

    • Database remains grayed out (30% opacity)
    • Large "✕ BYPASSED" or "Not called" label appears
    • No arrow reaches the Database
    • Visual emphasis: Make it very clear the DB is NOT touched
  8. SDK replays span from cache (0.5s)

    • Glow/pulse inside SDK border area
    • Label: Span replayed or Using cached response
    • Data passed: SDK serves the cached response from memory (loaded in step 1)
    • Response comes from SDK's memory, not from Database or Cloud
  9. Service → Tusk CLI (0.7s)

    • Arrow animates from Service to CLI
    • Label: { id: "1", title: "Post title" }
    • Data passed: Same response as Record mode, but from cache
  10. Test success (0.4s)

    • Checkmark appears near CLI
    • Label: ✓ Test passed or ✓ API test complete
    • Test validates response matches expectations
  11. Pause (1.5s): Before looping back to Record mode

Key Visual: Database is grayed out with X mark - SDK actively intercepts and serves cached data

REPLAY Mode Emphasis:

  • Tusk CLI is the test orchestrator (not just a passive client)
  • Database is clearly bypassed: Grayed out, ✕ mark, no arrows complete to it
  • SDK actively blocks: Shows intercepting action with strong pulse
  • Cached data comes from SDK's memory: Already fetched from Cloud in step 1
  • Faster execution: Whole sequence is quicker than RECORD (no real DB latency)

Transitions

  • Record → Replay: 0.5s crossfade, database transitions to grayscale, "Client" becomes "Tusk CLI"
  • Replay → Record: 0.5s crossfade, database transitions to color, "Tusk CLI" becomes "Client"

Tech Stack

Core Technologies

  • React (via Vite): Fast development with HMR
  • Motion (motion/react): Declarative animation library (formerly Framer Motion)
  • Tailwind CSS: Utility-first styling with custom Tusk purple (#9900ff)
  • SVG: Vector graphics for all visual elements
  • Puppeteer: Headless browser automation for GIF generation
  • ffmpeg: GIF compilation and optimization

Why These Choices?

  • Motion over CSS animations: Declarative, AI-friendly, easier to orchestrate complex sequences
  • SVG over Canvas: Scalable, crisp at any resolution, easier to position and style
  • Tailwind: Inline utility classes keep styling colocated with components
  • Puppeteer + ffmpeg: Automated, reproducible GIF generation with compression

Project Structure

src/
├── App.jsx                 # Mode switcher (Record ↔ Replay)
├── components/
│   ├── RecordMode.jsx      # Record animation sequence
│   ├── ReplayMode.jsx      # Replay animation sequence
│   ├── ServiceBox.jsx      # Service + SDK border
│   ├── Database.jsx        # Cylinder with active/bypassed states
│   ├── TuskCloud.jsx       # Cloud icon with pulse effects
│   ├── ClientBox.jsx       # Client/CLI box (changes color by mode)
│   ├── ExternalService.jsx # External service box (optional)
│   └── AnimatedArrow.jsx   # Reusable arrow with labels
├── index.css               # Tailwind imports + base styles
└── main.jsx                # React entry point

generate-gif.js             # Puppeteer script to create GIF
output/
└── tusk-animation.gif      # Generated animation (gitignored)

Installation & Setup

Prerequisites

  • Node.js - Version specified in .nvmrc file
    • If you have nvm installed, simply run: nvm use
    • Otherwise, ensure you have Node.js v18+ installed
  • npm (v9+)
  • ffmpeg: Required for GIF generation
    # macOS
    brew install ffmpeg
    
    # Ubuntu/Debian
    sudo apt install ffmpeg
    
    # Windows (via Chocolatey)
    choco install ffmpeg

Install Dependencies

npm install

Installs:

  • react, react-dom: UI framework
  • motion: Animation library
  • tailwindcss, @tailwindcss/postcss: Styling
  • puppeteer: Browser automation (downloads Chromium automatically)
  • vite: Build tool and dev server

Running the Animation

Development Mode

Start the live development server with hot reloading:

npm run dev

Then open http://localhost:5173 in your browser. The animation will loop continuously, switching between Record and Replay modes every 7 and 6 seconds respectively.

Generate GIF

To create the animated GIF:

node generate-gif.js

What it does:

  1. Launches headless Chrome via Puppeteer
  2. Opens http://localhost:5173
  3. Captures 140 frames at 10fps over 14 seconds (one complete loop)
  4. Compiles frames into a GIF using ffmpeg with optimized palette
  5. Outputs to ./output/tusk-animation.gif

Default settings:

  • Frame rate: 10fps (every 100ms)
  • Duration: 14 seconds (Record 7s + Replay 6s + transitions 1s)
  • Resolution: 1200×500px
  • File size: ~0.2-0.3 MB (well under 5MB limit)

Adjusting GIF Quality

Edit generate-gif.js to change settings:

Higher Quality / Larger File

const FRAME_RATE = 15; // 15fps instead of 10fps
const DURATION = 14000; // Keep duration same

This increases smoothness but file size (~0.4-0.5 MB).

Higher Resolution

await page.setViewport({ width: 1600, height: 600 }); // Larger canvas

Update SVG viewBox in components accordingly.

Longer Duration (Multiple Loops)

const DURATION = 28000; // 2 full loops (28 seconds)

Better Compression

The ffmpeg command in generate-gif.js already uses optimized settings:

  • scale=1200:-1:flags=lanczos: High-quality scaling
  • palettegen=max_colors=128: Optimized color palette
  • paletteuse=dither=bayer: Smooth dithering

To reduce file size further:

'palettegen=max_colors=64' // Fewer colors (was 128)

To increase quality:

'palettegen=max_colors=256' // More colors (was 128)

Animation Implementation Approach

1. Component-Based Architecture

Each visual element is a self-contained React component with its own animation variants:

// Example: ServiceBox with pulse animation
const serviceBoxVariants = {
  idle: { opacity: 1 },
  glow: {
    filter: [
      'drop-shadow(0 0 0px rgba(153, 0, 255, 0))',
      'drop-shadow(0 0 12px rgba(153, 0, 255, 0.6))',
      'drop-shadow(0 0 0px rgba(153, 0, 255, 0))'
    ],
    transition: { duration: 0.4 }
  }
};

2. Declarative Animation Sequencing

Using Motion's staggerChildren and delayChildren for automatic timing:

const recordSequence = {
  hidden: {},
  visible: {
    transition: {
      staggerChildren: 0.5  // Each step starts 0.5s after previous
    }
  }
}

3. Arrow Animations

Arrows use SVG pathLength animation for smooth drawing effect:

const pathVariants = {
  hidden: { pathLength: 0, opacity: 0 },
  visible: {
    pathLength: 1,
    opacity: 1,
    transition: { duration: 0.8, ease: "easeInOut" }
  }
};

4. Mode Switching

AnimatePresence provides smooth crossfade between modes:

<AnimatePresence mode="wait">
  <motion.div
    key={mode}
    initial={{ opacity: 0 }}
    animate={{ opacity: 1 }}
    exit={{ opacity: 0 }}
    transition={{ duration: 0.5 }}
  >
    {mode === 'record' ? <RecordMode /> : <ReplayMode />}
  </motion.div>
</AnimatePresence>

5. Precise Positioning

All coordinates calculated from box dimensions for pixel-perfect alignment:

const clientRightX = clientX + clientWidth;
const serviceCenterX = serviceX + serviceWidth / 2;
// Arrows connect at exact box edges

Key Visual Contrasts (Record vs Replay)

Aspect RECORD Mode REPLAY Mode
Database Full color (blue), active, pulses Grayed (30% opacity), ✕ mark, static
SDK Border Subtle pulse (passive observation) Strong pulse (active interception)
Arrows to DB Solid arrows that complete Arrows show attempt but are intercepted
Requester "Client" (dark gray box) "Tusk CLI" (purple box)
Cloud Timing Receives at END (step 8-9) Sends at BEGINNING (step 1)
Overall Feel Real API call (slower, hits DB) Fast test (DB bypassed, cached)

Customization

Changing Colors

Edit tailwind.config.js:

theme: {
  extend: {
    colors: {
      'tusk-purple': '#9900ff',  // Change brand color
    }
  }
}

Or update component fill colors directly in SVG:

fill="#9900ff"  // Tusk purple
fill="#3b82f6"  // Database blue
fill="#9ca3af"  // Service gray

Adjusting Timing

Edit the mode durations in src/App.jsx:

const duration = mode === 'record' ? 7000 : 6000; // milliseconds

Edit individual step durations in RecordMode.jsx and ReplayMode.jsx:

delay={1.1}      // When to start
duration={0.8}   // How long it takes

Adding New Elements

  1. Create new component in src/components/
  2. Add to RecordMode.jsx and/or ReplayMode.jsx
  3. Define position and animation variants
  4. Add arrows connecting to other elements

Troubleshooting

GIF Generation Issues

"ffmpeg: command not found"

  • Install ffmpeg (see Prerequisites)

"Error: Failed to launch the browser process" (Puppeteer)

  • Run: npm install puppeteer (reinstalls Chromium)
  • Or set PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=false

GIF file size too large

  • Reduce FRAME_RATE in generate-gif.js
  • Reduce max_colors in ffmpeg palette generation
  • Capture fewer frames or shorter duration

Animation Issues

Arrows not aligned

  • Check box dimensions match position calculations
  • Verify edge coordinates: boxX + width for right edge, boxX for left edge

Timing feels off

  • Adjust delay values in AnimatedArrow components
  • Modify mode duration in App.jsx
  • Check staggerChildren in animation variants

Components overlapping

  • Update x/y positions in mode components
  • Adjust SVG viewBox size if needed

Performance Notes

  • Development: Animations run at 60fps in browser
  • GIF Export: Captured at 10fps to keep file size small
  • File size: ~0.2MB at 10fps, 1200×500px, 128 colors
  • Generation time: ~18-20 seconds for 14-second animation

Enhancements

  • Fix spacing of arrows
  • Fix sizes and locations of objects
  • Add external service interactions (currently simplified)
  • Implement more detailed span visualization

Resources

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors