or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

app-snapshots.mdbuild-management.mdconfiguration.mdcore-operations.mdimage-uploads.mdindex.mdprogrammatic-api.mdstatic-snapshots.md

image-uploads.mddocs/

0

# Image Uploads

1

2

Direct upload of image files as visual snapshots, useful for integrating with external screenshot tools or uploading pre-captured images to Percy for visual comparison.

3

4

## Capabilities

5

6

### Percy Upload

7

8

Upload a directory of images directly to Percy as snapshots. Perfect for integrating with external screenshot tools, mobile app screenshots, or any workflow that generates image files.

9

10

```bash { .api }

11

/**

12

* Upload directory of images as snapshots

13

* Takes pre-captured images and uploads them to Percy for visual testing

14

*

15

* Usage: percy upload <dirname>

16

*

17

* Arguments:

18

* dirname Directory containing images to upload (required)

19

*

20

* Options:

21

* --files, -f <pattern> Glob patterns matching image files (multiple)

22

* Default: **/*.{png,jpg,jpeg}

23

* --ignore, -i <pattern> Glob patterns matching files to ignore (multiple)

24

* --strip-extensions, -e Strip file extensions from snapshot names

25

*

26

* Supported formats: .png, .jpg, .jpeg

27

* Supported projects: web and generic token types

28

*/

29

percy upload <dirname>

30

```

31

32

## Basic Usage

33

34

### Simple Directory Upload

35

36

Upload all images in a directory using default patterns:

37

38

```bash

39

# Upload all PNG, JPG, JPEG files

40

percy upload ./screenshots

41

42

# Directory structure:

43

# screenshots/

44

# ├── homepage.png → snapshot: "homepage.png"

45

# ├── about-page.jpg → snapshot: "about-page.jpg"

46

# ├── contact-form.jpeg → snapshot: "contact-form.jpeg"

47

# └── dashboard.png → snapshot: "dashboard.png"

48

```

49

50

### Custom File Patterns

51

52

Control which files are uploaded using glob patterns:

53

54

```bash

55

# Upload only PNG files

56

percy upload ./screenshots --files "**/*.png"

57

58

# Upload multiple specific patterns

59

percy upload ./screenshots \

60

--files "**/*.png" \

61

--files "**/mobile-*.jpg" \

62

--files "**/desktop-*.jpg"

63

64

# Upload from subdirectories

65

percy upload ./screenshots --files "**/final/*.{png,jpg}"

66

```

67

68

### Ignore Patterns

69

70

Exclude specific files or directories:

71

72

```bash

73

# Ignore temporary and backup files

74

percy upload ./screenshots \

75

--ignore "**/*-temp.*" \

76

--ignore "**/*.bak" \

77

--ignore "**/drafts/**"

78

79

# Ignore test and debug images

80

percy upload ./screenshots \

81

--ignore "**/test-*" \

82

--ignore "**/debug/**" \

83

--ignore "**/*.tmp"

84

```

85

86

### Clean Snapshot Names

87

88

Remove file extensions from snapshot names for cleaner URLs:

89

90

```bash

91

# Without strip-extensions

92

percy upload ./screenshots

93

# Creates: "homepage.png", "about-page.jpg", "contact.jpeg"

94

95

# With strip-extensions

96

percy upload ./screenshots --strip-extensions

97

# Creates: "homepage", "about-page", "contact"

98

```

99

100

## Integration Examples

101

102

### Playwright Screenshot Integration

103

104

```javascript

105

// playwright-screenshots.js

106

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

107

import path from 'path';

108

109

test.describe('Visual tests', () => {

110

test('capture screenshots', async ({ page }) => {

111

const screenshotDir = './percy-screenshots';

112

113

// Homepage

114

await page.goto('https://example.com');

115

await page.screenshot({

116

path: path.join(screenshotDir, 'homepage.png'),

117

fullPage: true

118

});

119

120

// About page

121

await page.goto('https://example.com/about');

122

await page.screenshot({

123

path: path.join(screenshotDir, 'about.png'),

124

fullPage: true

125

});

126

});

127

});

128

```

129

130

```bash

131

# Run Playwright tests to generate screenshots

132

npx playwright test

133

134

# Upload screenshots to Percy

135

percy upload ./percy-screenshots --strip-extensions

136

```

137

138

### Puppeteer Integration

139

140

```javascript

141

// puppeteer-screenshots.js

142

import puppeteer from 'puppeteer';

143

import fs from 'fs';

144

145

const browser = await puppeteer.launch();

146

const page = await browser.newPage();

147

148

// Ensure screenshot directory exists

149

fs.mkdirSync('./screenshots', { recursive: true });

150

151

// Capture multiple viewport sizes

152

const viewports = [

153

{ width: 375, height: 667, name: 'mobile' },

154

{ width: 768, height: 1024, name: 'tablet' },

155

{ width: 1280, height: 720, name: 'desktop' }

156

];

157

158

const pages = ['/', '/about', '/contact'];

159

160

for (const viewport of viewports) {

161

await page.setViewport(viewport);

162

163

for (const pagePath of pages) {

164

await page.goto(`https://example.com${pagePath}`);

165

const filename = `${pagePath.replace('/', 'home')}-${viewport.name}.png`;

166

await page.screenshot({

167

path: `./screenshots/${filename}`,

168

fullPage: true

169

});

170

}

171

}

172

173

await browser.close();

174

```

175

176

```bash

177

# Generate screenshots

178

node puppeteer-screenshots.js

179

180

# Upload to Percy

181

percy upload ./screenshots --strip-extensions

182

```

183

184

### Selenium Integration

185

186

```python

187

# selenium_screenshots.py

188

from selenium import webdriver

189

from selenium.webdriver.chrome.options import Options

190

import os

191

192

# Setup Chrome driver

193

options = Options()

194

options.add_argument('--headless')

195

driver = webdriver.Chrome(options=options)

196

197

# Create screenshots directory

198

os.makedirs('./screenshots', exist_ok=True)

199

200

# Test scenarios

201

scenarios = [

202

{'url': 'https://example.com', 'name': 'homepage'},

203

{'url': 'https://example.com/about', 'name': 'about'},

204

{'url': 'https://example.com/contact', 'name': 'contact'}

205

]

206

207

# Capture screenshots at different viewport sizes

208

viewports = [(375, 667), (768, 1024), (1280, 720)]

209

210

for width, height in viewports:

211

driver.set_window_size(width, height)

212

viewport_name = f"{width}x{height}"

213

214

for scenario in scenarios:

215

driver.get(scenario['url'])

216

filename = f"{scenario['name']}-{viewport_name}.png"

217

driver.save_screenshot(f"./screenshots/{filename}")

218

219

driver.quit()

220

```

221

222

```bash

223

# Generate screenshots

224

python selenium_screenshots.py

225

226

# Upload to Percy

227

percy upload ./screenshots --strip-extensions

228

```

229

230

### Mobile App Screenshots

231

232

```bash

233

# iOS Simulator screenshots (using xcrun simctl)

234

xcrun simctl io booted screenshot ./mobile-screenshots/ios-home.png

235

xcrun simctl io booted screenshot ./mobile-screenshots/ios-profile.png

236

237

# Android Emulator screenshots (using adb)

238

adb shell screencap -p /sdcard/android-home.png

239

adb pull /sdcard/android-home.png ./mobile-screenshots/

240

adb shell screencap -p /sdcard/android-profile.png

241

adb pull /sdcard/android-profile.png ./mobile-screenshots/

242

243

# Upload mobile screenshots

244

percy upload ./mobile-screenshots --strip-extensions

245

```

246

247

### External Tool Integration

248

249

```bash

250

# Storybook screenshot addon

251

npm run storybook:build

252

npm run storybook:screenshots # Generates screenshots

253

254

# Upload Storybook screenshots

255

percy upload ./storybook-screenshots \

256

--files "**/*.png" \

257

--ignore "**/temp/**" \

258

--strip-extensions

259

260

# BackstopJS integration

261

backstop test # Generates reference screenshots

262

263

# Upload BackstopJS screenshots

264

percy upload ./backstop_data/bitmaps_reference \

265

--files "**/*.png" \

266

--strip-extensions

267

```

268

269

## Advanced Configuration

270

271

### Complex Directory Structure

272

273

```bash

274

# Organized screenshot directory

275

screenshots/

276

├── desktop/

277

│ ├── homepage-1280x720.png

278

│ ├── about-1280x720.png

279

│ └── contact-1280x720.png

280

├── tablet/

281

│ ├── homepage-768x1024.png

282

│ ├── about-768x1024.png

283

│ └── contact-768x1024.png

284

├── mobile/

285

│ ├── homepage-375x667.png

286

│ ├── about-375x667.png

287

│ └── contact-375x667.png

288

└── temp/

289

└── draft-screenshot.png

290

291

# Upload with specific patterns

292

percy upload ./screenshots \

293

--files "**/desktop/*.png" \

294

--files "**/tablet/*.png" \

295

--files "**/mobile/*.png" \

296

--ignore "**/temp/**" \

297

--strip-extensions

298

```

299

300

### Naming Conventions

301

302

```bash

303

# Descriptive filenames become snapshot names

304

screenshots/

305

├── 01-homepage-desktop.png → "01-homepage-desktop"

306

├── 02-about-page-mobile.png → "02-about-page-mobile"

307

├── 03-contact-form-tablet.png → "03-contact-form-tablet"

308

├── 04-dashboard-logged-in.png → "04-dashboard-logged-in"

309

└── 05-profile-settings.png → "05-profile-settings"

310

311

percy upload ./screenshots --strip-extensions

312

```

313

314

### Batch Processing

315

316

```bash

317

# Upload multiple screenshot directories

318

for dir in ./test-results/*/screenshots/; do

319

if [ -d "$dir" ]; then

320

percy upload "$dir" --strip-extensions

321

fi

322

done

323

324

# Conditional upload based on file count

325

screenshot_count=$(find ./screenshots -name "*.png" | wc -l)

326

if [ "$screenshot_count" -gt 0 ]; then

327

echo "Uploading $screenshot_count screenshots"

328

percy upload ./screenshots --strip-extensions

329

else

330

echo "No screenshots found"

331

fi

332

```

333

334

## File Requirements

335

336

### Supported Formats

337

338

- **PNG**: Preferred format, supports transparency

339

- **JPG/JPEG**: Supported, good for photographs

340

- **Other formats**: Not supported (GIF, WebP, SVG, etc.)

341

342

### File Size Considerations

343

344

- **Maximum file size**: 25MB per image

345

- **Recommended size**: Under 5MB for optimal upload performance

346

- **Dimensions**: No hard limits, but very large images may timeout

347

348

### Quality Guidelines

349

350

```bash

351

# Good screenshot practices:

352

# - Use PNG for UI screenshots (better compression for flat colors)

353

# - Use consistent viewport sizes

354

# - Capture full page or specific components consistently

355

# - Remove dynamic content (timestamps, ads) before capturing

356

# - Use descriptive filenames

357

```

358

359

## Error Handling

360

361

### Common Upload Issues

362

363

```bash

364

# Handle missing directory

365

if [ ! -d "./screenshots" ]; then

366

echo "Screenshots directory not found"

367

exit 1

368

fi

369

370

# Check for supported file types

371

image_count=$(find ./screenshots -name "*.png" -o -name "*.jpg" -o -name "*.jpeg" | wc -l)

372

if [ "$image_count" -eq 0 ]; then

373

echo "No supported image files found"

374

exit 1

375

fi

376

377

# Upload with error handling

378

percy upload ./screenshots --strip-extensions || {

379

echo "Percy upload failed"

380

exit 1

381

}

382

```

383

384

### File Access Issues

385

386

```bash

387

# Ensure proper permissions

388

chmod -R 644 ./screenshots/*.{png,jpg,jpeg}

389

390

# Check file accessibility

391

find ./screenshots -name "*.png" -not -readable | while read file; do

392

echo "Warning: Cannot read $file"

393

done

394

```

395

396

## Integration Patterns

397

398

### CI/CD Pipeline

399

400

```yaml

401

# GitHub Actions example

402

- name: Generate screenshots

403

run: npm run screenshots

404

405

- name: Upload to Percy

406

env:

407

PERCY_TOKEN: ${{ secrets.PERCY_TOKEN }}

408

run: percy upload ./screenshots --strip-extensions

409

```

410

411

### Development Workflow

412

413

```bash

414

# Development script

415

#!/bin/bash

416

set -e

417

418

echo "Cleaning old screenshots..."

419

rm -rf ./screenshots

420

421

echo "Generating new screenshots..."

422

npm run test:visual

423

424

echo "Uploading to Percy..."

425

percy upload ./screenshots --strip-extensions

426

427

echo "Screenshots uploaded successfully!"

428

```

429

430

### Multi-Environment Testing

431

432

```bash

433

# Upload screenshots from different environments

434

percy upload ./screenshots/staging --files "**/*.png" --strip-extensions

435

percy upload ./screenshots/production --files "**/*.png" --strip-extensions

436

```

437

438

## Token Requirements

439

440

### Project Types

441

442

- **Web projects**: Standard Percy tokens work normally

443

- **Generic projects**: Self-managed/BYOS tokens also supported

444

- **App projects**: Native app tokens supported for mobile screenshots

445

446

### Configuration

447

448

```bash

449

# Set Percy token

450

export PERCY_TOKEN=your-percy-token

451

452

# Verify token type

453

percy ping # Should respond if token is valid

454

```