Token导航 LogoToken导航TokenDH.com
研究检索操作浏览器github未标认证来源可访问clear审计通过

mobile-emulation移动仿真

Agent Skill

mobile-emulation 用于查找、检索和筛选相关信息,适合在 Codex、Claude、Cursor、Gemini CLI 中需要根据关键词、任务场景或来源线索快速定位候选结果时使用。可结合来源仓库、安装命令和原始 README 继续核验具体用法。安装前建议确认权限范围、维护状态,以及是否会触发联网、命令执行或文件读写。

总安装

504

周安装

21

GitHub Stars

9

下载量

168
CodexClaudeCursorGemini CLI

安装说明

本站只整理中文说明和来源信息,不托管安装包,也不代用户安装。

GitHub

来源数

3

许可证

MIT

最后核验

2026-05-01

来源状态

来源可访问

安装方式

通过对话安装

复制提示词发给支持本地命令或 Skills 的 AI 助手,先确认命令和权限,再让它执行。

请帮我安装这个 Agent Skill:mobile-emulation(移动仿真)
来源仓库:https://github.com/adaptationio/skrillz
仓库路径:skills/mobile-emulation
安装命令:
npx skills add https://github.com/adaptationio/skrillz --skill mobile-emulation
安装前请先检查当前环境是否支持对应 CLI,并向我确认将要执行的命令、安装目录、联网范围和文件读写权限;确认后再执行。

命令行安装

复制命令到本机终端执行。不同来源提供的安装方式可能略有差异;本站展示可直接复制的安装命令,安装前请核对来源页面。

skills.shnpx skills
npx skills add https://github.com/adaptationio/skrillz --skill mobile-emulation

简介

用于查找、检索和筛选移动仿真相关信息。

  • 适合根据关键词或任务场景快速定位候选结果。
  • 可结合来源仓库 README 核验具体用法和功能范围。
  • 安装前建议确认权限范围和是否会触发联网操作。
  • 需注意维护状态和执行边界。mobile-emulation 属于研究检索类 Skill,可作为该场景下的辅助能力补充。

SKILL.md

Mobile Emulation Testing with Playwright

Test responsive designs and mobile-specific features using Playwright's device emulation capabilities.

Quick Start

import { test, expect, devices } from '@playwright/test';

test.use(devices['iPhone 14']);

test('mobile navigation works', async ({ page }) => {
  await page.goto('/');

  // Mobile menu should be visible
  await page.getByRole('button', { name: 'Menu' }).click();
  await expect(page.getByRole('navigation')).toBeVisible();
});

Configuration

Project-Based Device Testing

playwright.config.ts:

import { defineConfig, devices } from '@playwright/test';

export default defineConfig({
  projects: [
    // Desktop browsers
    {
      name: 'Desktop Chrome',
      use: { ...devices['Desktop Chrome'] },
    },
    {
      name: 'Desktop Firefox',
      use: { ...devices['Desktop Firefox'] },
    },
    {
      name: 'Desktop Safari',
      use: { ...devices['Desktop Safari'] },
    },

    // Mobile devices
    {
      name: 'iPhone 14',
      use: { ...devices['iPhone 14'] },
    },
    {
      name: 'iPhone 14 Pro Max',
      use: { ...devices['iPhone 14 Pro Max'] },
    },
    {
      name: 'Pixel 7',
      use: { ...devices['Pixel 7'] },
    },
    {
      name: 'Galaxy S23',
      use: { ...devices['Galaxy S III'] },  // Closest available
    },

    // Tablets
    {
      name: 'iPad Pro',
      use: { ...devices['iPad Pro 11'] },
    },
    {
      name: 'iPad Mini',
      use: { ...devices['iPad Mini'] },
    },
  ],
});

Custom Device Configuration

{
  name: 'Custom Mobile',
  use: {
    viewport: { width: 390, height: 844 },
    userAgent: 'Mozilla/5.0 (iPhone; CPU iPhone OS 17_0 like Mac OS X)...',
    deviceScaleFactor: 3,
    isMobile: true,
    hasTouch: true,
    defaultBrowserType: 'webkit',
  },
},

Available Devices

Popular Devices

import { devices } from '@playwright/test';

// iPhones
devices['iPhone 14']
devices['iPhone 14 Plus']
devices['iPhone 14 Pro']
devices['iPhone 14 Pro Max']
devices['iPhone 13']
devices['iPhone 12']
devices['iPhone SE']

// Android Phones
devices['Pixel 7']
devices['Pixel 5']
devices['Galaxy S III']
devices['Galaxy S5']
devices['Galaxy Note 3']
devices['Nexus 5']

// Tablets
devices['iPad Pro 11']
devices['iPad Pro 11 landscape']
devices['iPad Mini']
devices['iPad (gen 7)']
devices['Galaxy Tab S4']

// Desktop
devices['Desktop Chrome']
devices['Desktop Firefox']
devices['Desktop Safari']
devices['Desktop Edge']

List All Devices

import { devices } from '@playwright/test';

console.log(Object.keys(devices));
// Outputs all available device names

Responsive Breakpoint Testing

Test Multiple Viewports

const breakpoints = [
  { name: 'mobile', width: 375, height: 667 },
  { name: 'tablet', width: 768, height: 1024 },
  { name: 'desktop', width: 1280, height: 720 },
  { name: 'wide', width: 1920, height: 1080 },
];

for (const bp of breakpoints) {
  test(`layout at ${bp.name}`, async ({ page }) => {
    await page.setViewportSize({ width: bp.width, height: bp.height });
    await page.goto('/');
    await expect(page).toHaveScreenshot(`layout-${bp.name}.png`);
  });
}

Dynamic Viewport Changes

test('responsive navigation', async ({ page }) => {
  await page.goto('/');

  // Desktop - horizontal nav
  await page.setViewportSize({ width: 1280, height: 720 });
  await expect(page.locator('.desktop-nav')).toBeVisible();
  await expect(page.locator('.mobile-menu-button')).not.toBeVisible();

  // Tablet - may show hamburger
  await page.setViewportSize({ width: 768, height: 1024 });

  // Mobile - hamburger menu
  await page.setViewportSize({ width: 375, height: 667 });
  await expect(page.locator('.desktop-nav')).not.toBeVisible();
  await expect(page.locator('.mobile-menu-button')).toBeVisible();
});

Touch Interactions

Tap

test('tap interaction', async ({ page }) => {
  await page.goto('/');

  // Tap is equivalent to click on touch devices
  await page.getByRole('button').tap();
});

Swipe

test('swipe carousel', async ({ page }) => {
  await page.goto('/gallery');

  const carousel = page.locator('.carousel');
  const box = await carousel.boundingBox();

  if (box) {
    // Swipe left
    await page.mouse.move(box.x + box.width - 50, box.y + box.height / 2);
    await page.mouse.down();
    await page.mouse.move(box.x + 50, box.y + box.height / 2, { steps: 10 });
    await page.mouse.up();
  }

  await expect(page.locator('.slide-2')).toBeVisible();
});

Pinch to Zoom

test('pinch to zoom map', async ({ page }) => {
  await page.goto('/map');

  const map = page.locator('#map');
  const box = await map.boundingBox();

  if (box) {
    const centerX = box.x + box.width / 2;
    const centerY = box.y + box.height / 2;

    // Simulate pinch out (zoom in)
    await page.touchscreen.tap(centerX, centerY);
    // Note: Multi-touch pinch requires custom implementation
  }
});

Long Press

test('long press context menu', async ({ page }) => {
  await page.goto('/');

  const element = page.locator('.long-press-target');
  const box = await element.boundingBox();

  if (box) {
    await page.mouse.move(box.x + box.width / 2, box.y + box.height / 2);
    await page.mouse.down();
    await page.waitForTimeout(1000);  // Hold for 1 second
    await page.mouse.up();
  }

  await expect(page.locator('.context-menu')).toBeVisible();
});

Orientation Testing

Portrait vs Landscape

test('orientation change', async ({ page }) => {
  // Portrait
  await page.setViewportSize({ width: 390, height: 844 });
  await page.goto('/video');
  await expect(page.locator('.video-container')).toHaveCSS('width', '390px');

  // Landscape
  await page.setViewportSize({ width: 844, height: 390 });
  await expect(page.locator('.video-container')).toHaveCSS('width', '844px');
});

Using Device Landscape Variants

test.use(devices['iPad Pro 11 landscape']);

test('tablet landscape layout', async ({ page }) => {
  await page.goto('/dashboard');

  // Sidebar should be visible in landscape
  await expect(page.locator('.sidebar')).toBeVisible();
});

Geolocation Testing

test.use({
  geolocation: { latitude: 40.7128, longitude: -74.0060 },  // NYC
  permissions: ['geolocation'],
});

test('shows nearby locations', async ({ page }) => {
  await page.goto('/locations');

  await page.getByRole('button', { name: 'Find Nearby' }).click();

  await expect(page.getByText('New York')).toBeVisible();
});

Change Location During Test

test('location change', async ({ page, context }) => {
  await context.setGeolocation({ latitude: 51.5074, longitude: -0.1278 });  // London
  await page.goto('/weather');
  await expect(page.getByText('London')).toBeVisible();

  await context.setGeolocation({ latitude: 35.6762, longitude: 139.6503 });  // Tokyo
  await page.reload();
  await expect(page.getByText('Tokyo')).toBeVisible();
});

Network Conditions

Slow 3G

test('works on slow network', async ({ page, context }) => {
  // Emulate slow 3G
  const client = await context.newCDPSession(page);
  await client.send('Network.emulateNetworkConditions', {
    offline: false,
    downloadThroughput: (500 * 1024) / 8,  // 500kb/s
    uploadThroughput: (500 * 1024) / 8,
    latency: 400,  // 400ms
  });

  await page.goto('/');

  // Should show skeleton loaders
  await expect(page.locator('.skeleton')).toBeVisible();

  // Eventually loads
  await expect(page.locator('.content')).toBeVisible({ timeout: 30000 });
});

Offline Mode

test('offline functionality', async ({ page, context }) => {
  await page.goto('/');

  // Cache page, then go offline
  await context.setOffline(true);

  await page.reload();

  // Should show offline message or cached content
  await expect(page.getByText(/offline/i)).toBeVisible();
});

Device-Specific Features

Notch/Safe Areas

test('respects safe areas', async ({ page }) => {
  // iPhone with notch
  test.use(devices['iPhone 14 Pro']);

  await page.goto('/');

  // Header should account for notch
  const header = page.locator('header');
  const paddingTop = await header.evaluate(el =>
    window.getComputedStyle(el).paddingTop
  );

  // Should have safe area inset
  expect(parseInt(paddingTop)).toBeGreaterThan(20);
});

Dark Mode

test.use({
  colorScheme: 'dark',
});

test('dark mode styling', async ({ page }) => {
  await page.goto('/');

  const body = page.locator('body');
  const bgColor = await body.evaluate(el =>
    window.getComputedStyle(el).backgroundColor
  );

  // Should have dark background
  expect(bgColor).toBe('rgb(0, 0, 0)');  // or dark color
});

Reduced Motion

test.use({
  reducedMotion: 'reduce',
});

test('respects reduced motion', async ({ page }) => {
  await page.goto('/');

  const animated = page.locator('.animated-element');
  const animationDuration = await animated.evaluate(el =>
    window.getComputedStyle(el).animationDuration
  );

  // Should have no animation
  expect(animationDuration).toBe('0s');
});

Visual Regression Across Devices

const testDevices = [
  'iPhone 14',
  'Pixel 7',
  'iPad Pro 11',
  'Desktop Chrome',
];

for (const deviceName of testDevices) {
  test.describe(`Visual: ${deviceName}`, () => {
    test.use(devices[deviceName]);

    test('homepage', async ({ page }) => {
      await page.goto('/');
      await expect(page).toHaveScreenshot(`homepage-${deviceName}.png`);
    });

    test('product page', async ({ page }) => {
      await page.goto('/products/1');
      await expect(page).toHaveScreenshot(`product-${deviceName}.png`);
    });
  });
}

Best Practices

  1. Test real devices too - Emulation is good but not perfect
  2. Cover major breakpoints - 375px, 768px, 1024px, 1280px minimum
  3. Test both orientations - Portrait and landscape
  4. Test touch vs click - Some interactions differ
  5. Test slow networks - Mobile users often have poor connectivity
  6. Test safe areas - Account for notches, home indicators

References

  • references/device-list.md - Complete device list with specs
  • references/touch-patterns.md - Touch gesture implementations

适合场景

01

用户想查找某类 Agent Skill 时

02

需要根据任务场景推荐可安装能力包时

03

需要对比不同来源的安装命令和来源信息时

04

需要参考平台分布和安装热度时

能力概览

能力 1

按任务关键词查找相关 Skills

能力 2

展示可复制的安装命令

能力 3

保留来源站点、仓库和原始说明,方便继续核验

能力 4

补充不同宿主或平台的使用分布数据

能力 5

展示第三方安全扫描或审计结果

安装后应在对应宿主中按原始 README 的触发条件使用;具体调用方式请以来源页面和 README 为准。

平台分布

github-copilot

32.03%
按下载量换算54

Claude Code

22.35%
按下载量换算38

mcpjam

16.65%
按下载量换算28

moltbot

12.63%
按下载量换算21

windsurf

7.6%
按下载量换算13

zencoder

3.13%
按下载量换算5

安全审计

Gen Agent Trust Hub

通过

Socket

通过

Snyk

通过

权限和风险

操作浏览器

该 Skill 可能涉及浏览器控制能力,使用时可能读取或操作网页内容,需要在受控环境中确认权限边界。

安装前确认

本站仅展示第三方公开信息,不托管安装包,不提供自动安装或运行环境。安装前应自行审查源码、依赖和命令行为。

来源信息

继续浏览同类 Skills