MCPcopy Index your code
hub / github.com/ctrlaltdylan/mock-bridge

github.com/ctrlaltdylan/mock-bridge @v1.2.6

Chat with this repo
repository ↗ · DeepWiki ↗ · release v1.2.6 ↗ · + Follow
337 symbols 877 edges 53 files 12 documented · 4% updated 7d agov1.2.6 · 2026-02-28★ 522 open issues
What it actually does AI analysis from the code graph — generated when you open this
loading…
README

Mock Bridge Logo

# Shopify Mock Bridge

A comprehensive browser testing solution for Shopify embedded apps. Mock the Shopify Admin environment and App Bridge APIs locally without needing real Shopify credentials, captchas, or 2FA.

npm version License: MIT

🎥 See It In Action

Mock Bridge Demo Video

▶️ Watch the Demo Video - See how Mock Bridge simplifies Shopify app testing

🎯 Why Use This Package?

Testing Shopify embedded apps is hard:

  • 🚫 Shopify Admin requires 2FA and captchas
  • 🤖 Playwright/automation tools can't bypass security
  • 🔒 Chrome DevTools MCP can't interact with your embedded Shopify app
  • 🌐 CI/CD pipelines need internet and credentials
  • 🐛 Manual testing in real admin is slow and unreliable

This package solves all of that:

  • ✅ No captchas, 2FA, or real Shopify account needed
  • ✅ Full Playwright and automation support
  • ✅ Chrome MCP and DevTools compatibility
  • ✅ Works offline and in CI/CD pipelines
  • ✅ Real database integration for comprehensive testing

🗺️ Roadmap

Core Features

  • ✅ iFrame embed
  • ✅ Session token issuance

App Bridge APIs

  • [ ] Direct admin API fetch overrides
  • [ ] Toast implementation
  • [ ] Resource picker
  • [ ] Dynamic scopes

📦 Installation

npm install @getverdict/mock-bridge --save-dev
# or
yarn add @getverdict/mock-bridge --dev
# or
pnpm add @getverdict/mock-bridge --save-dev

⚡ Quick Start (2 Commands)

# 1. Install the package
npm install @getverdict/mock-bridge --save-dev

# 2. Start the mock (just provide your app URL)
npx @getverdict/mock-bridge http://localhost:3000

That's it! Your app is now running in a mock Shopify Admin at http://localhost:3080

🚀 Quick Start Guide

Step 1: Start the Mock Server

Option A: One-Command Start (Recommended)

# Simplest - just provide your app URL
npx @getverdict/mock-bridge http://localhost:3000

# Auto-detects client ID from SHOPIFY_API_KEY environment variable
# Auto-detects common app paths and configurations

Option B: Configuration File

# Generate a config file
npx @getverdict/mock-bridge init

# Edit the generated mock.config.js, then run:
npx @getverdict/mock-bridge
# Add to package.json scripts for easy access
{
  "scripts": {
    "dev": "next dev",
    "mock:admin": "mock-bridge http://localhost:3000",
    "dev:mock": "concurrently \"npm run dev\" \"npm run mock:admin\""
  }
}

Option C: Programmatic API

If you need more control, you can still use the programmatic API:

// scripts/start-mock-admin.js
const { MockShopifyAdminServer } = require("@verdict/mock-bridge");

async function startMockAdmin() {
  const server = new MockShopifyAdminServer({
    appUrl: "http://localhost:3000",
    clientId: process.env.SHOPIFY_API_KEY,
    clientSecret: "mock-secret-12345",
    port: 3080,
    debug: true,
  });

  await server.start();
  console.log("🎉 Mock Shopify Admin ready at http://localhost:3080");
}

startMockAdmin().catch(console.error);

Step 2: Frontend Integration

Enable your app to detect and use the mock environment:

Option A: Automatic Detection (Recommended)

Replace your App Bridge script loading:


<script src="https://cdn.shopify.com/shopifycloud/app-bridge.js"></script>


<script>
  // Check if we're in a mock environment
  const urlParams = new URLSearchParams(window.location.search);
  const isEmbedded = urlParams.get("embedded") === "1";
  const host = urlParams.get("host");

  let isMockEnvironment = false;

  // Detect mock environment from URL parameters
  if (isEmbedded && host) {
    try {
      const decodedHost = atob(host);
      if (
        decodedHost.includes("localhost") ||
        decodedHost.includes("mock") ||
        window.location.hostname === "localhost"
      ) {
        isMockEnvironment = true;
      }
    } catch (e) {
      // Ignore decode errors
    }
  }

  // Load appropriate App Bridge
  if (isMockEnvironment) {
    console.log("Loading Mock App Bridge");
    const script = document.createElement("script");
    script.src = "http://localhost:3080/app-bridge.js";
    script.onerror = () => {
      // Fallback to real CDN if mock fails
      const fallback = document.createElement("script");
      fallback.src = "https://cdn.shopify.com/shopifycloud/app-bridge.js";
      document.head.appendChild(fallback);
    };
    document.head.appendChild(script);
  } else {
    console.log("Loading Real Shopify App Bridge");
    const script = document.createElement("script");
    script.src = "https://cdn.shopify.com/shopifycloud/app-bridge.js";
    document.head.appendChild(script);
  }
</script>

Option B: Package Utility (TypeScript)

// app.tsx or _app.tsx
import { setupAppBridge } from "@getverdict/mock-bridge/client";

useEffect(() => {
  setupAppBridge({
    debug: true,
    onMockDetected: (mockServerUrl) => {
      console.log("Mock environment detected:", mockServerUrl);
    },
    onShopifyDetected: () => {
      console.log("Real Shopify environment detected");
    },
  })
    .then(() => {
      console.log("App Bridge loaded successfully");
    })
    .catch(console.error);
}, []);

Step 3: Backend Integration

Make your backend support mock session tokens alongside real ones:

Quick Integration (Replace existing JWT validation)

// Before: Only real Shopify tokens
import { verifyShopifyJWT } from "./your-auth";

export async function authenticate(token: string) {
  const authData = await verifyShopifyJWT(token);
  // ... rest of auth logic
}
// After: Support both real and mock tokens
import {
  validateSessionToken,
  createMockUser,
} from "@getverdict/mock-bridge/auth";

export async function authenticate(token: string) {
  const authData = await validateSessionToken(token, {
    shopifySecret: process.env.SHOPIFY_API_PRIVATE_KEY!,
  });

  if (!authData) {
    throw new Error("Invalid token");
  }

  // Get shop from your database (same for both mock and real)
  const shop = await getShopByName(authData.shopName);
  if (!shop) {
    throw new Error("Shop not found");
  }

  if (authData.isMock) {
    // Mock environment - skip Shopify API calls
    return {
      shop,
      currentUser: createMockUser({
        shopName: authData.shopName,
        permissions: shop.settings?.defaultStaffPermissions,
      }),
      isMock: true,
    };
  } else {
    // Real environment - proceed with normal Shopify flow
    const currentUser = await exchangeTokenForUser(token, shop);
    return {
      shop,
      currentUser,
      isMock: false,
    };
  }
}

Framework-Specific Examples

Next.js API Routes:

// pages/api/products.ts
import { validateSessionToken } from "@getverdict/mock-bridge/auth";

export default async function handler(req, res) {
  const token = req.headers.authorization?.replace("Bearer ", "");

  const authData = await validateSessionToken(token, {
    shopifySecret: process.env.SHOPIFY_API_PRIVATE_KEY!,
  });

  if (!authData) {
    return res.status(401).json({ error: "Unauthorized" });
  }

  if (authData.isMock) {
    // Return mock data for testing
    return res.json({
      products: [
        { id: "1", title: "Mock Product 1", price: "19.99" },
        { id: "2", title: "Mock Product 2", price: "29.99" },
      ],
    });
  } else {
    // Fetch real products from Shopify
    const products = await fetchShopifyProducts(authData.shopName);
    return res.json({ products });
  }
}

Express.js Middleware:

import {
  validateSessionToken,
  createMockUser,
} from "@getverdict/mock-bridge/auth";

function createAuthMiddleware() {
  return async (req, res, next) => {
    const token = req.headers.authorization?.replace("Bearer ", "");

    const authData = await validateSessionToken(token, {
      shopifySecret: process.env.SHOPIFY_API_PRIVATE_KEY!,
    });

    if (!authData) {
      return res.status(401).json({ error: "Unauthorized" });
    }

    const shop = await getShopByName(authData.shopName);
    req.shop = shop;
    req.isMockAuth = authData.isMock;

    if (authData.isMock) {
      req.currentUser = createMockUser({ shopName: authData.shopName });
    } else {
      req.currentUser = await exchangeTokenForUser(token, shop);
    }

    next();
  };
}

app.use("/api/*", createAuthMiddleware());

Step 4: Database Setup

Ensure your mock shop exists in the database:

// Add this to your database seed or setup script
async function setupMockShop() {
  const mockShop = {
    name: "test-shop.myshopify.com",
    displayName: "Mock Test Shop",
    accessToken: "mock-access-token",
    active: true,
    settings: {
      defaultStaffPermissions: [
        "read_products",
        "write_products",
        "read_orders",
        "write_orders",
      ],
    },
  };

  await createOrUpdateShop(mockShop);
  console.log("Mock shop created for testing");
}

// Run during development setup
if (process.env.NODE_ENV === "development") {
  setupMockShop();
}

Step 5: Start Development

# Option 1: CLI command (simplest)
npx @getverdict/mock-bridge http://localhost:3000

# Option 2: Package.json scripts
npm run dev:mock

# Option 3: Separate terminals
npm run dev                    # Terminal 1: Your app
npm run mock:admin            # Terminal 2: Mock admin

Then navigate to:

  • Your app: http://localhost:3000
  • Mock Shopify Admin: http://localhost:3080
  • Your app embedded in mock admin: http://localhost:3080 (automatically embeds your app)

🖥️ CLI Reference

Quick Commands

# Basic usage with auto-detection
npx @getverdict/mock-bridge http://localhost:3000

# If installed locally, you can use the shorter command:
# npm install @getverdict/mock-bridge --save-dev
# npx mock-bridge http://localhost:3000

# Full configuration
npx @getverdict/mock-bridge http://localhost:3000/shopify \
  --client-id your-client-id \
  --port 3080 \
  --debug

# Using config file
npx @getverdict/mock-bridge init           # Create config file
npx @getverdict/mock-bridge                # Use config file

# Help and version
npx @getverdict/mock-bridge --help
npx @getverdict/mock-bridge --version

CLI Options

Option Description Default
[app-url] Your app's URL (positional argument) Auto-detected from package.json
--client-id Shopify app client ID $SHOPIFY_API_KEY
--client-secret Mock client secret "mock-secret-12345"
--shop Mock shop domain "test-shop.myshopify.com"
--port Mock admin port 3080
--config Config file path "mock.config.js"
--debug Enable debug logging false

Environment Variables

The CLI automatically reads these environment variables:

SHOPIFY_API_KEY=your-client-id      # Used for --client-id
NODE_ENV=development                # Enables mock token support

Configuration File

Generate a configuration file with npx @getverdict/mock-bridge init:

// mock.config.js
module.exports = {
  appUrl: "http://localhost:3000/shopify", // Include path in URL
  clientId: process.env.SHOPIFY_API_KEY,
  clientSecret: "mock-secret-12345",
  port: 3080,
  shop: "test-shop.myshopify.com",
  debug: true,
  scopes: ["read_products", "write_products", "read_orders", "write_orders"],
};

🎭 How It Works

Architecture Overview

┌─────────────────────────────────────┐
│   Mock Shopify Admin (Port 3080)   │
│  ┌─────────────────────────────┐   │
│  │     Shopify Admin UI         │   │
│  │     (Navigation, etc.)       │   │
│  └─────────────────────────────┘   │
│  ┌─────────────────────────────┐   │
│  │   Your App (iframe)          │   │  ← Embedded like real Shopify
│  │   - Mock App Bridge loaded   │   │
│  │   - Gets mock session tokens │   │
│  │   - Makes API calls to your  │   │
│  │     backend                  │   │
│  └─────────────────────────────┘   │
└─────────────────────────────────────┘
         ↕ PostMessage API
┌─────────────────────────────────────┐
│    Your Backend                     │
│    - Validates mock tokens          │  ← Same backend, enhanced auth
│    - Skips Shopify API calls        │
│    - Uses real database             │
│    - Returns mock/real data         │
└─────────────────────────────────────┘

Mock vs Real Flow

Mock Environment (Testing):

  1. Mock admin serves your app in iframe
  2. Mock App Bridge provides session tokens
  3. Your frontend makes API calls to your backend
  4. Backend detects mock tokens and skips Shopify APIs
  5. Returns mock data or uses database directly

Real Environment (Production):

  1. Real Shopify admin serves your app in iframe
  2. Real App Bridge provides session tokens
  3. Your frontend makes API calls to your backend
  4. Backend detects real tokens and calls Shopify APIs
  5. Returns real data from Shopify

🔧 Configuration Options

Mo

Extension points exported contracts — how you extend this code

Config (Interface)
(no doc)
admin-frame/src/hooks/useConfig.ts
MockEnvironmentConfig (Interface)
(no doc)
src/client/mock-detector.ts
MockShopifyAdminConfig (Interface)
(no doc)
src/types/index.ts
MockTokenHandlerOptions (Interface)
(no doc)
src/auth/withMockTokenSupport.ts
CLIConfig (Interface)
(no doc)
src/cli/index.ts
Window (Interface)
(no doc)
test-app/src/main.ts
MockWebhook (Interface)
(no doc)
src/types/index.ts
AuthResult (Interface)
(no doc)
src/auth/validateSessionToken.ts

Core symbols most depended-on inside this repo

addEventListener
called by 59
app-bridge/prod-app-bridge.js
i
called by 40
app-bridge/prod-app-bridge.js
e
called by 29
app-bridge/prod-app-bridge.js
o
called by 26
app-bridge/prod-app-bridge.js
removeEventListener
called by 22
app-bridge/prod-app-bridge.js
invokeFeature
called by 22
app-bridge/src/invokeFeature.ts
o
called by 20
app-bridge/prod-app-bridge.js
Ut
called by 17
app-bridge/prod-app-bridge.js

Shape

Function 234
Method 65
Interface 22
Class 16

Languages

TypeScript100%

Modules by API surface

app-bridge/prod-app-bridge.js224 symbols
src/types/index.ts11 symbols
src/auth/token-generator.ts9 symbols
src/server/index.ts8 symbols
src/client/mock-detector.ts8 symbols
src/cli/index.ts7 symbols
app-bridge/src/features/modal.ts7 symbols
test-app/src/main.ts5 symbols
app-bridge/src/features/save-bar.ts5 symbols
app-bridge/src/features/nav-menu.ts5 symbols
src/auth/createMockUser.ts4 symbols
src/auth/withMockTokenSupport.ts3 symbols

For agents

$ claude mcp add mock-bridge \
  -- python -m otcore.mcp_server <graph>

⬇ download graph artifact