Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 76 additions & 0 deletions .github/workflows/publish_extension.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
name: Publish Extension to Chrome Web Store
on:
workflow_dispatch:

jobs:
publish-extension-chrome-web-store:
runs-on: ubuntu-latest
environment: allow-publishing-extension-to-cws
steps:
- uses: actions/checkout@v5
- uses: actions/setup-node@v5
with:
node-version: 20
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Build extension
run: npm run build-extension
- name: Package extension
working-directory: ./packages/extension
run: |
cd dist
zip -r ../extension.zip .
- name: Get access token
id: auth
run: |
ACCESS_TOKEN=$(curl -s -X POST "https://oauth2.googleapis.com/token" \
-d "client_id=${{ secrets.CHROME_CLIENT_ID }}" \
-d "client_secret=${{ secrets.CHROME_CLIENT_SECRET }}" \
-d "refresh_token=${{ secrets.CHROME_REFRESH_TOKEN }}" \
-d "grant_type=refresh_token" | jq -r '.access_token')
if [ "$ACCESS_TOKEN" = "null" ] || [ -z "$ACCESS_TOKEN" ]; then
echo "Failed to obtain access token"
exit 1
fi
echo "::add-mask::$ACCESS_TOKEN"
echo "access_token=$ACCESS_TOKEN" >> $GITHUB_OUTPUT
- name: Upload to Chrome Web Store
run: |
RESPONSE=$(curl -s -w "\n%{http_code}" \
-X PUT "https://www.googleapis.com/upload/chromewebstore/v1.1/items/${{ secrets.CHROME_EXTENSION_ID }}" \
-H "Authorization: Bearer ${{ steps.auth.outputs.access_token }}" \
-H "x-goog-api-version: 2" \
-T ./packages/extension/extension.zip)
HTTP_CODE=$(echo "$RESPONSE" | tail -1)
BODY=$(echo "$RESPONSE" | head -n -1)
echo "$BODY"
if [ "$HTTP_CODE" -ne 200 ]; then
echo "Upload failed with HTTP $HTTP_CODE"
exit 1
fi
STATUS=$(echo "$BODY" | jq -r '.uploadState')
if [ "$STATUS" != "SUCCESS" ]; then
echo "Upload state: $STATUS"
exit 1
fi
- name: Publish to Chrome Web Store
run: |
RESPONSE=$(curl -s -w "\n%{http_code}" \
-X POST "https://www.googleapis.com/chromewebstore/v1.1/items/${{ secrets.CHROME_EXTENSION_ID }}/publish" \
-H "Authorization: Bearer ${{ steps.auth.outputs.access_token }}" \
-H "x-goog-api-version: 2" \
-H "Content-Length: 0")
HTTP_CODE=$(echo "$RESPONSE" | tail -1)
BODY=$(echo "$RESPONSE" | head -n -1)
echo "$BODY"
if [ "$HTTP_CODE" -ne 200 ]; then
echo "Publish failed with HTTP $HTTP_CODE"
exit 1
fi
STATUS=$(echo "$BODY" | jq -r '.status[0]')
if [ "$STATUS" != "OK" ] && [ "$STATUS" != "PUBLISHED_WITH_FRICTION_WARNING" ]; then
echo "Publish status: $STATUS"
exit 1
fi
echo "Extension published successfully"
49 changes: 49 additions & 0 deletions .github/workflows/tests_extension.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
name: Extension

on:
push:
branches:
- main
- release-*
paths:
- 'packages/playwright-core/src/tools/**'
- '!packages/playwright-core/src/tools/dashboard/**'
- '!packages/playwright-core/src/tools/trace/**'
- 'packages/extension/**'
- 'tests/extension/**'
- '.github/workflows/tests_extension.yml'
pull_request:
branches:
- main
- release-*
paths:
- 'packages/playwright-core/src/tools/**'
- '!packages/playwright-core/src/tools/dashboard/**'
- '!packages/playwright-core/src/tools/trace/**'
- 'packages/extension/**'
- 'tests/extension/**'
- '.github/workflows/tests_extension.yml'

env:
FORCE_COLOR: 1
ELECTRON_SKIP_BINARY_DOWNLOAD: 1
DEBUG: pw:mcp:error

jobs:
test_extension:
name: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [macos-latest]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v6
- uses: actions/setup-node@v6
with:
node-version: 22
- run: npm ci
- run: npm run build
- run: npm run build-extension
- run: npx playwright install --with-deps
- run: npm run test-extension
48 changes: 48 additions & 0 deletions docs/src/api/class-locator.md
Original file line number Diff line number Diff line change
Expand Up @@ -922,6 +922,54 @@ Locator of the element to drag to.
### option: Locator.dragTo.steps = %%-input-drag-steps-%%
* since: v1.57

## async method: Locator.drop
* since: v1.60

Simulate an external drag-and-drop of files or clipboard-like data onto this locator.

**Details**

Dispatches the native `dragenter`, `dragover`, and `drop` events at the center of the
target element with a synthetic [DataTransfer] carrying the provided files and/or data
entries. Works cross-browser by constructing the [DataTransfer] in the page context.

If the target element's `dragover` listener does not call `preventDefault()`, the target
is considered to have rejected the drop: Playwright dispatches `dragleave` and this
method throws.

**Usage**

Drop a file buffer onto an upload area:

```js
await page.locator('#dropzone').drop({
files: { name: 'note.txt', mimeType: 'text/plain', buffer: Buffer.from('hello') },
});
```

Drop plain text and a URL together:

```js
await page.locator('#dropzone').drop({
data: {
'text/plain': 'hello world',
'text/uri-list': 'https://example.com',
},
});
```

### param: Locator.drop.payload = %%-drop-payload-%%
* since: v1.60

### option: Locator.drop.position = %%-input-position-%%
* since: v1.60

### option: Locator.drop.timeout = %%-input-timeout-%%
* since: v1.60

### option: Locator.drop.timeout = %%-input-timeout-js-%%
* since: v1.60

## async method: Locator.elementHandle
* since: v1.14
* discouraged: Always prefer using [Locator]s and web assertions over [ElementHandle]s because latter are inherently racy.
Expand Down
12 changes: 12 additions & 0 deletions docs/src/api/params.md
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,18 @@ Defaults to `left`.
- `mimeType` <[string]> File type
- `buffer` <[Buffer]> File content

## drop-payload
- `payload` <[Object]>
- `files` ?<[path]|[Array]<[path]>|[Object]|[Array]<[Object]>>
- `name` <[string]> File name
- `mimeType` <[string]> File type
- `buffer` <[Buffer]> File content
- `data` ?<[Object]<[string], [string]>>

Data to drop onto the target. Provide `files` (file paths or in-memory buffers), `data`
(a mime-type → string map for clipboard-like content such as `text/plain`, `text/html`,
`text/uri-list`), or both.

## input-down-up-delay
- `delay` <[float]>

Expand Down
88 changes: 88 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@
"test": "playwright test --config=tests/library/playwright.config.ts",
"test-mcp": "playwright test --config=tests/mcp/playwright.config.ts",
"ctest-mcp": "playwright test --config=tests/mcp/playwright.config.ts --project=chrome",
"test-extension": "playwright test --config=tests/extension/playwright.config.ts",
"build-extension": "npm run build -w @playwright/extension",
"eslint": "eslint --cache",
"tsc": "tsc -p .",
"doc": "node utils/doclint/cli.js",
Expand Down
15 changes: 12 additions & 3 deletions packages/dashboard/src/dashboard.css
Original file line number Diff line number Diff line change
Expand Up @@ -154,17 +154,26 @@
padding: 0 12px;
font-size: 13px;
font-family: inherit;
background: var(--color-canvas-default);
background: var(--color-canvas-inset);
color: var(--color-fg-default);
border: 1px solid var(--color-border-muted);
border: 1px solid var(--color-border-default);
border-radius: 16px;
outline: none;
min-width: 0;
}

:root.light-mode .omnibox {
background: var(--color-canvas-default);
}

.omnibox:focus {
border-color: var(--color-accent-fg);
background: var(--color-canvas-subtle);
background: var(--color-canvas-default);
box-shadow: 0 0 0 2px var(--color-accent-muted);
}

:root.light-mode .omnibox:focus {
background: var(--color-canvas-default);
}

.omnibox::placeholder {
Expand Down
Loading
Loading