-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* working on clipboard copy example * working clipboard * add example with clipboard copy and paste * add the new recipe to the root files * show how to spy on the clipboard method * add more clipboard tests * split the longer test into several smaller ones * add cypress-repeat * add cy.screenshot * skip tests when not in Electron * add test that puts text into clipboard * add permissions spec * grant clipboard permission in chrome using CDP * add chrome on CI tests * playing with code * bump the chrome browser image * add link to the recorded video * remove screenshots * add more notes about permissions * add more comments
- Loading branch information
Showing
11 changed files
with
390 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
# Clipboard | ||
> Copy / paste text example | ||
The widget to copy text is `@github/clipboard-copy-element` custom element that comes from [github/github-elements](https://github.com/github/github-elements) | ||
|
||
![Copy / paste test](./images/copy-paste.gif) | ||
|
||
See the [cypress/integration/spec.js](./cypress/integration/spec.js) file. The test currently work only in Electron where the clipboard permission is granted when Cypress starts it. | ||
|
||
The page [index.html](./index.html) shows the copy button on "mouseover" event. When the text is copied to the clipboard, it shows a [tiny toast](https://github.com/bahmutov/tiny-toast) popup. | ||
|
||
See the [Mozilla Clipboard docs](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/Interact_with_the_clipboard) | ||
|
||
## Other browsers | ||
|
||
Work in progress, mostly because the default permissions prompt the user to allow the page to access the clipboard. | ||
|
||
## Videos | ||
|
||
We show how to test the clipboard access from Cypress in these videos: | ||
|
||
- [Access the clipboard from Cypress test using Electron browser](https://youtu.be/SExmed1dCL4) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
{ | ||
"fixturesFolder": false, | ||
"supportFile": false, | ||
"pluginsFile": false, | ||
"viewportWidth": 400, | ||
"viewportHeight": 300 | ||
} |
74 changes: 74 additions & 0 deletions
74
examples/testing-dom__clipboard/cypress/integration/permissions-spec.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
/// <reference types="cypress" /> | ||
|
||
// Permissions API specifically for clipboard information | ||
// https://web.dev/async-clipboard/ | ||
// https://developer.mozilla.org/en-US/docs/Web/API/Permissions/query | ||
|
||
/* eslint-env browser */ | ||
describe('Clipboard permissions', () => { | ||
// Electron has access to the clipboard | ||
// https://www.electronjs.org/docs/api/clipboard#clipboard | ||
it('are granted in Electron', { browser: 'electron' }, () => { | ||
cy.visit('index.html') // yields the window object | ||
.its('navigator.permissions') | ||
// permission names taken from | ||
// https://w3c.github.io/permissions/#enumdef-permissionname | ||
.invoke('query', { name: 'clipboard-read' }) | ||
.its('state') | ||
.should('equal', 'granted') | ||
}) | ||
|
||
// we can safely query the current permission status in Chrome | ||
it('can be queried in Chrome', { browser: 'chrome' }, () => { | ||
cy.visit('index.html') // yields the window object | ||
.its('navigator.permissions') | ||
// permission names taken from | ||
// https://w3c.github.io/permissions/#enumdef-permissionname | ||
.invoke('query', { name: 'clipboard-read' }) | ||
// by default it is "prompt" which shows a popup asking | ||
// the user if the site can have access to the clipboard | ||
// if the user allows, then next time it will be "granted" | ||
// If the user denies access to the clipboard, on the next | ||
// run the state will be "denied" | ||
.its('state').should('be.oneOf', ['prompt', 'granted', 'denied']) | ||
}) | ||
|
||
it('can be granted in Chrome', { browser: 'chrome' }, () => { | ||
// use the Chrome debugger protocol to grant the current browser window | ||
// access to the clipboard from the current origin | ||
// https://chromedevtools.github.io/devtools-protocol/tot/Browser/#method-grantPermissions | ||
// We are using cy.wrap to wait for the promise returned | ||
// from the Cypress.automation call, so the test continues | ||
// after the clipboard permission has been granted | ||
cy.wrap(Cypress.automation('remote:debugger:protocol', { | ||
command: 'Browser.grantPermissions', | ||
params: { | ||
permissions: ['clipboardReadWrite', 'clipboardSanitizedWrite'], | ||
// make the permission tighter by allowing the current origin only | ||
// like "http://localhost:56978" | ||
origin: window.location.origin, | ||
}, | ||
})) | ||
|
||
cy.visit('index.html') // yields the window object | ||
.its('navigator.permissions') | ||
// permission names taken from | ||
// https://w3c.github.io/permissions/#enumdef-permissionname | ||
.invoke('query', { name: 'clipboard-read' }) | ||
.its('state').should('equal', 'granted') | ||
|
||
// now reading the clipboard from test will work | ||
// but only via navigator.clipboard - the document.execCommand | ||
// does nothing. | ||
cy.get('code').trigger('mouseover') | ||
cy.get('[aria-label="Copy"]').click() | ||
// confirm the clipboard's contents | ||
cy.window().its('navigator.clipboard') | ||
.invoke('readText') | ||
.should('equal', 'npm install -D cypress') | ||
|
||
// TODO how can we paste the clipboard into the text area? | ||
// right now the document.execCommand('paste') does not | ||
// do anything in the Chrome browser | ||
}) | ||
}) |
92 changes: 92 additions & 0 deletions
92
examples/testing-dom__clipboard/cypress/integration/spec.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
/// <reference types="cypress" /> | ||
|
||
// access to the clipboard reliably works in Electron browser | ||
// in other browsers, there are popups asking for permission | ||
// thus we should only run these tests in Electron | ||
describe('Clipboard', { browser: 'electron' }, () => { | ||
it('copies text to clipboard', () => { | ||
cy.visit('index.html') | ||
cy.get('code').trigger('mouseover') | ||
cy.get('[aria-label="Copy"]').click() | ||
|
||
// let's check the copied text | ||
// on Chrome this operation will prompt the browser | ||
// to ask the user for permission: | ||
// http://localhost:port wants to | ||
// See text and images copied the clipboard | ||
cy.window().its('navigator.clipboard') | ||
.invoke('readText') | ||
.should('equal', 'npm install -D cypress') | ||
}) | ||
|
||
it('shows the popup', () => { | ||
cy.visit('index.html') | ||
cy.get('code').trigger('mouseover') | ||
cy.get('[aria-label="Copy"]').click() | ||
|
||
cy.contains('.tinyToast', 'Copied!').should('be.visible') | ||
// the toast then goes away in less than 2 seconds | ||
cy.get('.tinyToast', { timeout: 2000 }).should('not.exist') | ||
}) | ||
|
||
it('can set the clipboard text in the text area', () => { | ||
cy.visit('index.html') | ||
cy.get('code').trigger('mouseover') | ||
cy.get('[aria-label="Copy"]').click() | ||
|
||
// let's check the copied text | ||
cy.window().its('navigator.clipboard') | ||
.invoke('readText') | ||
.should('equal', 'npm install -D cypress') | ||
.then((text) => { | ||
// paste the text from the clipboard into the text area | ||
cy.get('#paste-here').click().invoke('val', text) | ||
}) | ||
}) | ||
|
||
it('spies on the clipboard methods', () => { | ||
cy.visit('index.html') | ||
cy.get('code').trigger('mouseover') | ||
cy.window().its('navigator.clipboard').then((clipboard) => { | ||
cy.spy(clipboard, 'writeText').as('writeText') | ||
}) | ||
|
||
cy.get('[aria-label="Copy"]').click() | ||
cy.get('@writeText') | ||
.should('have.been.calledOnceWith', 'npm install -D cypress') | ||
}) | ||
|
||
it('falls back to document.execCommand if navigator does not support clipboard', () => { | ||
cy.visit('index.html', { | ||
onBeforeLoad (win) { | ||
// tip: to correctly delete a property from | ||
// the navigator, must delete it from its prototype | ||
delete win.navigator.__proto__.clipboard | ||
}, | ||
}) | ||
|
||
cy.document().then((doc) => cy.spy(doc, 'execCommand').as('execCommand')) | ||
cy.get('code').trigger('mouseover') | ||
cy.get('[aria-label="Copy"]').click() | ||
cy.get('@execCommand').should('have.been.calledOnceWith', 'copy') | ||
|
||
// we can paste the clipboard text | ||
cy.get('#paste-here').focus() | ||
cy.document().invoke('execCommand', 'paste') | ||
cy.get('#paste-here').should('have.value', 'npm install -D cypress') | ||
}) | ||
|
||
it('writes text into clipboard', () => { | ||
cy.visit('index.html') | ||
// the document has to have focus before we can | ||
// write our text into the clipboard | ||
cy.get('#paste-here').focus() | ||
cy.window() | ||
.its('navigator.clipboard') | ||
.invoke('writeText', 'this is a test') | ||
|
||
// paste the clipboard into the text area | ||
cy.document().invoke('execCommand', 'paste') | ||
cy.get('#paste-here').should('have.value', 'this is a test') | ||
}) | ||
}) |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,114 @@ | ||
<head> | ||
<script src="https://unpkg.com/@github/clipboard-copy-element@latest" defer></script> | ||
<script src="https://unpkg.com/[email protected]/dist/tiny-toast.js" defer></script> | ||
<style> | ||
pre { | ||
padding: 16px; | ||
overflow: auto; | ||
font-size: 85%; | ||
line-height: 1.45; | ||
background-color: #f6f8fa; | ||
border-radius: 6px; | ||
} | ||
|
||
.snippet-clipboard-content { | ||
font-family: -apple-system,BlinkMacSystemFont,Segoe UI,Helvetica,Arial,sans-serif,Apple Color Emoji,Segoe UI Emoji; | ||
font-size: 16px; | ||
line-height: 1.5; | ||
word-wrap: break-word; | ||
box-sizing: border-box; | ||
position: relative!important; | ||
} | ||
.zeroclipboard-container { | ||
display: none; | ||
animation: fade-out .2s both; | ||
} | ||
|
||
.right-0 { | ||
right: 0!important; | ||
} | ||
.top-0 { | ||
top: 0!important; | ||
} | ||
.position-relative { | ||
position: relative!important; | ||
} | ||
.position-absolute { | ||
position: absolute!important; | ||
} | ||
.p-0 { | ||
padding:0!important; | ||
} | ||
.m-2 { | ||
margin: 8px!important; | ||
} | ||
.ClipboardButton { | ||
position: relative; | ||
} | ||
.btn { | ||
position: relative; | ||
display: inline-block; | ||
padding: 5px 16px; | ||
font-size: 14px; | ||
font-weight: 500; | ||
line-height: 20px; | ||
white-space: nowrap; | ||
vertical-align: middle; | ||
cursor: pointer; | ||
-webkit-user-select: none; | ||
-moz-user-select: none; | ||
-ms-user-select: none; | ||
user-select: none; | ||
border: 1px solid; | ||
border-radius: 6px; | ||
-webkit-appearance: none; | ||
-moz-appearance: none; | ||
appearance: none; | ||
} | ||
.btn .octicon { | ||
color: lightgray; | ||
margin-right: 4px; | ||
vertical-align: text-bottom; | ||
} | ||
.octicon { | ||
display: inline-block; | ||
overflow: visible!important; | ||
vertical-align: text-bottom; | ||
} | ||
</style> | ||
</head> | ||
<body> | ||
<h1>Clipboard</h1> | ||
|
||
<div class="snippet-clipboard-content position-relative"> | ||
<pre><code>$ npm install -D cypress</code></pre> | ||
<div class="zeroclipboard-container position-absolute right-0 top-0"> | ||
<clipboard-copy aria-label="Copy" | ||
class="ClipboardButton btn js-clipboard-copy m-2 p-0 tooltipped-no-delay" | ||
data-copy-feedback="Copied!" | ||
data-tooltip-direction="w" | ||
value="npm install -D cypress" tabindex="0" role="button"> | ||
<svg aria-hidden="true" viewBox="0 0 16 16" version="1.1" data-view-component="true" height="16" width="16" class="octicon octicon-clippy js-clipboard-clippy-icon m-2"> | ||
<path fill-rule="evenodd" d="M5.75 1a.75.75 0 00-.75.75v3c0 .414.336.75.75.75h4.5a.75.75 0 00.75-.75v-3a.75.75 0 00-.75-.75h-4.5zm.75 3V2.5h3V4h-3zm-2.874-.467a.75.75 0 00-.752-1.298A1.75 1.75 0 002 3.75v9.5c0 .966.784 1.75 1.75 1.75h8.5A1.75 1.75 0 0014 13.25v-9.5a1.75 1.75 0 00-.874-1.515.75.75 0 10-.752 1.298.25.25 0 01.126.217v9.5a.25.25 0 01-.25.25h-8.5a.25.25 0 01-.25-.25v-9.5a.25.25 0 01.126-.217z"></path> | ||
</svg> | ||
</clipboard-copy> | ||
</div> | ||
</div> | ||
|
||
<textarea id="paste-here" rows="4" cols="40"></textarea> | ||
|
||
<script> | ||
const contents = document.querySelector('.snippet-clipboard-content') | ||
const clipboardContainer = contents.querySelector('.zeroclipboard-container') | ||
|
||
contents.addEventListener('mouseover', (e) => { | ||
clipboardContainer.style.display = 'inherit' | ||
}) | ||
contents.addEventListener('mouseout', (e) => { | ||
clipboardContainer.style.display = 'none' | ||
}) | ||
document.addEventListener('clipboard-copy', function(event) { | ||
tinyToast.show('Copied!').hide(1500) | ||
}) | ||
</script> | ||
</body> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
{ | ||
"name": "clipboard", | ||
"version": "1.0.0", | ||
"description": "Copy / paste text example", | ||
"private": true, | ||
"scripts": { | ||
"cypress:open": "../../node_modules/.bin/cypress open", | ||
"cypress:run": "../../node_modules/.bin/cypress run", | ||
"start": "echo nothing to start", | ||
"test:ci": "npm run cypress:run", | ||
"test:ci:chrome": "../../node_modules/.bin/cypress run --browser chrome", | ||
"test:ci:chrome:headless": "../../node_modules/.bin/cypress run --browser chrome --headless", | ||
"test:ci:record": "npm run cypress:run -- --record", | ||
"test:repeat": "../../node_modules/.bin/cypress-repeat -n 5" | ||
} | ||
} |
Oops, something went wrong.