Design Tool Testing Guide
How to Test Figma File Actions with Playwright: Complete 2026 Guide
A scenario-by-scenario walkthrough of testing Figma file actions with Playwright. Canvas rendering challenges, REST API fallback assertions, file browser navigation, sharing and permission controls, plugin loading lifecycle, and the pitfalls that break real Figma test suites.
“Figma reports over four million paying customers as of 2024, with design files rendered entirely on a WebGL canvas that has zero inspectable DOM nodes for traditional selectors.”
Figma File Action End-to-End Flow
1. Why Testing Figma File Actions Is Harder Than It Looks
Figma renders its entire design canvas using WebGL. Unlike traditional web applications where every button, text input, and list item is a DOM node you can query with CSS selectors or Playwright locators, the Figma canvas is a single <canvas> element. Every frame, component, text layer, and vector shape is drawn as pixels inside that canvas. There is no getByText("Rectangle 1") for objects on the artboard. This is the fundamental challenge: you cannot use standard DOM assertions for anything that lives on the canvas.
The complexity extends beyond the canvas itself. Figma's file browser (the dashboard at figma.com) is a standard React application with inspectable DOM nodes, but it uses virtualized lists that only render visible rows. Scrolling to a file that is off-screen requires programmatic scrolling and waiting for new rows to mount. Sharing dialogs use modal overlays with dynamic permission dropdowns that are not in the DOM until opened. Plugin loading spawns an isolated iframe sandbox that communicates with the main thread through a postMessage bridge, and the plugin UI elements live inside that iframe, requiring frameLocator in Playwright.
There are five structural reasons this flow is hard to test reliably. First, canvas rendering means no DOM nodes for design objects, so you must fall back to the Figma REST API to verify canvas state. Second, the file browser uses virtualized scrolling, so files off-screen do not exist in the DOM until you scroll to them. Third, sharing and permission modals are lazily rendered and destroyed when closed. Fourth, plugins run in sandboxed iframes with their own document context. Fifth, Figma aggressively rate limits API calls (roughly 30 requests per minute per token), so your test suite must batch assertions carefully.
Figma File Action Architecture
File Browser
React app, virtualized lists
Open File
Navigate to /file/:key
Canvas Init
WebGL renderer boots
Toolbar + Panels
DOM-based UI shell
User Action
Rename, share, export
API Persist
REST or WebSocket sync
Plugin Loading Flow
User
Runs plugin from menu
App Shell
Creates iframe sandbox
Plugin Code
Executes in sandbox
postMessage
Plugin <-> Main thread
Canvas Update
Plugin modifies nodes
A robust Figma test suite combines DOM-based Playwright assertions for the app shell (toolbar, panels, modals) with REST API calls to verify canvas state. The sections below walk through each scenario with runnable TypeScript you can copy directly.
2. Setting Up Your Test Environment
Before writing any file action tests, you need three things: a Figma account with at least one team and project, a personal access token for the REST API, and a dedicated test file that your suite can safely modify without affecting real design work. Figma provides a free Starter plan that supports REST API access and plugin development. Create a dedicated test team to isolate your automation from production files.
Figma Test Environment Setup Checklist
- Create a dedicated Figma team for E2E tests (separate from production)
- Generate a personal access token at figma.com/developers
- Create a test file with known structure (frames, components, text layers)
- Note the file key from the URL: figma.com/file/:fileKey/:fileName
- Create a test project folder within the team
- Install @figma/rest-api-spec or use raw fetch for API calls
- Store credentials in .env.test (never commit tokens)
- Configure Playwright to persist auth cookies across tests
Environment Variables
Figma REST API Helper
Since the canvas is not inspectable via DOM, you need a helper that calls the Figma REST API to verify file state. This helper wraps the most common endpoints: get file metadata, get file nodes, get file versions, and get project files. Rate limiting is critical; Figma enforces roughly 30 requests per minute per personal access token.
Playwright Configuration for Figma
Figma's canvas takes time to initialize the WebGL context. Set generous navigation and action timeouts. Use storageState to persist Figma session cookies so you only log in once per test run.
3. Scenario: Navigating the File Browser
The Figma file browser at figma.com is the starting point for most file actions. It displays teams, projects, and files in a virtualized grid or list. The challenge is that files outside the visible viewport are not in the DOM. You need to scroll programmatically, wait for new file cards to render, and then locate your target. The search functionality is a faster alternative: type in the search bar and wait for results to appear.
Navigate to a Specific File via Search
StraightforwardGoal
From the Figma dashboard, search for a file by name, click the result, and confirm the file editor opens with the correct file loaded on the canvas.
Preconditions
- Authenticated Figma session via
storageState - Test file exists with a known name in the test team
Playwright Implementation
What to Assert Beyond the UI
The canvas is visible, but you cannot confirm what is rendered on it through DOM inspection. Use the REST API to verify the file key matches.
4. Scenario: Renaming and Duplicating a File
Renaming a Figma file happens in the toolbar at the top of the editor. The file name is an editable text input. Duplicating a file is accessible through the file menu or the right-click context menu on the dashboard. Both actions persist through the Figma backend and can be verified through the REST API. The tricky part is that rename is debounced; Figma does not save the new name on every keystroke. You need to trigger a blur event or press Enter to commit the change, then wait for the API to reflect it.
Rename a File from the Editor Toolbar
ModerateGoal
Open a test file, rename it via the toolbar input, and confirm the new name persists both in the UI and in the REST API response.
Preconditions
- Authenticated Figma session
- Test file open in the editor
- User has edit access to the file
Playwright Implementation
Rename File: Playwright vs Assrt
import { test, expect } from '@playwright/test';
import { getFile } from '../helpers/figma-api';
test('rename file from toolbar', async ({ page }) => {
const fileKey = process.env.FIGMA_TEST_FILE_KEY!;
const newName = `Renamed ${Date.now()}`;
await page.goto(`/file/${fileKey}`);
await page.waitForSelector('canvas', {
state: 'visible',
timeout: 30_000,
});
const filenameInput = page.getByTestId('filename-input');
await filenameInput.click({ clickCount: 3 });
await filenameInput.fill(newName);
await filenameInput.press('Enter');
await page.waitForTimeout(3_000);
const file = await getFile(fileKey);
expect(file.name).toBe(newName);
});Duplicate a File from the Dashboard
ModerateGoal
From the file browser, right-click a file, select “Duplicate”, and verify a copy appears in the project with the expected naming convention (original name followed by “(Copy)”).
Playwright Implementation
6. Scenario: Asserting Canvas State via the REST API
This is the core challenge unique to Figma testing. When a user creates a frame, adds text, or moves an element on the canvas, those changes are invisible to Playwright's DOM queries. The canvas is a bitmap. Your only reliable option for verifying canvas state is the Figma REST API. The GET /v1/files/:key/nodes endpoint returns the full node tree for specific node IDs, including position, size, text content, fills, and child relationships. You can query this endpoint after a UI action to confirm the change persisted.
The timing challenge is significant. After a user performs an action on the canvas (for example, renaming a text layer), Figma does not immediately persist the change to the API. There is a sync delay, typically between 1 and 5 seconds, before the REST API reflects the latest state. Your test must include a polling mechanism or a fixed wait to account for this delay. Polling is preferable because it is faster on average and more reliable than a fixed sleep.
Verify a Text Layer via REST API
ComplexGoal
After a UI action that modifies a text layer on the canvas, poll the Figma REST API to confirm the change persisted in the file's node tree.
Playwright Implementation
7. Scenario: Loading and Running a Plugin
Figma plugins run in an isolated iframe sandbox. When a user launches a plugin from the Resources menu (or via the quick actions search), Figma creates a sandboxed iframe that loads the plugin's UI. The plugin communicates with the main Figma application through a postMessage bridge. From a Playwright perspective, this means plugin UI elements are inside a different frame context. You must use frameLocator to access plugin DOM nodes.
The plugin loading sequence has several stages that can fail silently. First, the plugin manifest is fetched. Second, the sandbox iframe is created. Third, the plugin code executes and renders its UI. If the plugin has a network dependency (for example, fetching data from an external API), that adds another asynchronous step. Each stage can introduce timing issues that cause flaky tests if you do not wait for the right signals.
Launch a Plugin and Interact with Its UI
ComplexGoal
Open a file, launch a known plugin from the menu, wait for its UI to appear in the plugin iframe, interact with a button inside the plugin, and verify the plugin action completed.
Preconditions
- Test file open in the Figma editor
- A known test plugin installed in the team (or published to the community)
- Plugin has a visible UI panel (not a run-once plugin)
Playwright Implementation
Plugin Loading: Playwright vs Assrt
test('launch plugin and interact', async ({ page }) => {
const fileKey = process.env.FIGMA_TEST_FILE_KEY!;
await page.goto(`/file/${fileKey}`);
await page.waitForSelector('canvas', {
state: 'visible', timeout: 30_000
});
await page.keyboard.press('Shift+i');
const search = page.getByPlaceholder(/search/i);
await search.fill('My Test Plugin');
await page.waitForTimeout(1_000);
await page.getByText('My Test Plugin').first().click();
await page.getByRole('button', { name: /run/i }).click();
const frame = page.frameLocator('iframe[name*="plugin"]');
const btn = frame.getByRole('button', { name: /generate/i });
await expect(btn).toBeVisible({ timeout: 10_000 });
await btn.click();
await expect(
frame.getByText(/done|success|complete/i)
).toBeVisible({ timeout: 15_000 });
});8. Scenario: Version History and Restore
Figma automatically saves versions as users work. Named versions can be created manually through the File menu or the version history panel. Testing version creation and restoration is important for workflows that rely on version snapshots (for example, saving a “before” state before a design review). The version history panel opens as a sidebar, and each version entry is a clickable row. Restoring a version triggers a confirmation dialog.
The Figma REST API provides a GET /v1/files/:key/versions endpoint that returns all named versions. You can use this to verify that a version was created with the correct label and timestamp. Combining the UI interaction (creating a named version) with an API assertion (verifying it appears in the versions list) gives you full confidence that the feature works end to end.
Create and Verify a Named Version
ModerateGoal
Create a named version through the Figma UI, then verify it appears in both the version history panel and the REST API response.
Playwright Implementation
9. Common Pitfalls That Break Figma Test Suites
Figma testing has a unique set of failure modes because of the canvas rendering architecture and the dynamic nature of the application shell. The following pitfalls come from real issues reported in Figma community forums, GitHub discussions about Figma plugin development, and common patterns observed in production test suites.
Common Pitfalls (Avoid These)
- Trying to query canvas objects with DOM selectors. Canvas elements have zero DOM presence. Use the REST API instead.
- Not accounting for Figma's API sync delay. Changes take 1 to 5 seconds to appear in REST responses. Always poll or add explicit waits.
- Exceeding the 30 requests/minute rate limit. Batch your API assertions and reuse GET responses across multiple expect() calls.
- Hardcoding pixel coordinates for canvas clicks without accounting for viewport zoom. Always reset zoom to 100% before coordinate-based interactions.
- Forgetting that plugin UIs run in sandboxed iframes. Use page.frameLocator() to access plugin DOM nodes, not page.getByRole() on the main frame.
- Assuming the file browser renders all files. Figma virtualizes the file list; only visible rows exist in the DOM. Use search or scroll programmatically.
- Not resetting the test file state between runs. Previous test runs may leave renamed files, extra collaborators, or unwanted versions. Add a cleanup step in globalSetup.
- Ignoring Figma's session cookie expiration. Personal access tokens last indefinitely, but browser sessions expire. Re-authenticate in globalSetup if cookies are stale.
Debugging Tip: Network Interception for Canvas Actions
When canvas-based actions seem to fail silently, intercept Figma's WebSocket traffic to see what the client is actually sending. Figma uses WebSockets for real-time collaboration and canvas sync. You can use Playwright's page.on('websocket') listener to log messages for debugging, although the binary protocol is not human-readable without Figma's internal schema.
10. Writing These Scenarios in Plain English with Assrt
The code samples above are detailed, but maintaining them is expensive. Figma ships updates frequently, and even minor UI changes (a renamed button, a restructured menu, a new modal layout) can silently break your Playwright selectors. Assrt lets you describe test scenarios in plain English. The Assrt compiler generates the same Playwright TypeScript you saw in the preceding sections, committed to your repo as real tests you can read, run, and modify.
Here is how the file browser navigation, rename, sharing, canvas assertion, and plugin loading scenarios look as a single .assrt file:
# figma-file-actions.assrt
config:
baseURL: https://www.figma.com
auth: storageState from ./test/.auth/figma-session.json
---
scenario: Navigate to file via search
steps:
- go to the Figma dashboard
- click the search button
- type "E2E Test File" in the search field
- click the first search result matching the file name
expect:
- the URL contains "/file/"
- a canvas element is visible
- the toolbar shows "E2E Test File" as the filename
---
scenario: Rename file from toolbar
steps:
- open the test file in Figma editor
- triple-click the filename in the toolbar to select it
- type a new unique file name
- press Enter to commit the rename
expect:
- the toolbar shows the new file name
- the Figma REST API returns the new name for this file key
---
scenario: Share file with edit access
steps:
- open the test file in Figma
- click the "Share" button in the toolbar
- type a collaborator email in the invite field
- press Enter
- change the permission to "can edit"
- click "Invite"
expect:
- the collaborator email appears in the share list
- their permission shows "can edit"
---
scenario: Verify canvas text via API
steps:
- open the test file in Figma
- double-click the known text layer position on the canvas
- select all text and type new content
- click outside to deselect
expect:
- the Figma REST API reflects the new text content for the target node ID
---
scenario: Launch plugin and interact
steps:
- open the test file in Figma
- press Shift+I to open Resources
- search for "My Test Plugin"
- click the plugin result
- click "Run"
- click the "Generate" button inside the plugin UI
expect:
- the plugin displays a success messageAssrt compiles each scenario block into the same Playwright TypeScript you saw in the preceding sections, committed to your repo as real tests you can read, run, and modify. When Figma renames a menu item from “Save to Version History” to “Add to Version History” or restructures the sharing modal, Assrt detects the failure, analyzes the new DOM, and opens a pull request with the updated locators. Your scenario files stay untouched.
Start with the file browser search scenario. Once it is green in your CI, add the rename test, then sharing permissions, then the canvas API assertion, then plugin loading, and finally version history. In a single afternoon you can have complete Figma file action coverage that accounts for the canvas rendering gap and the dynamic app shell that most teams never manage to test by hand.
Related Guides
How to Test File Upload
A practical, scenario-by-scenario guide to testing file upload forms with Playwright....
How to Test S3 Presigned URL Upload
A practical guide to testing S3 presigned URL uploads with Playwright. Covers CORS...
How to Fix Flaky Tests
Root causes and proven fixes for unreliable tests.
Ready to automate your testing?
Assrt discovers test scenarios, writes Playwright tests from plain English, and self-heals when your UI changes.