17

How to get the 'clipboard' content in cypress. I have a button in my web application, on click of button system will perform 'copy to clipboard' and a message will get displayed. Below is an example of the url content that is copy to clipboard ( this url content is different from web site url)

https://someurl.net/machines/0c459829-a5b1-4d4b-b3c3-18b03c1c969a/attachments/a30ceca7-198e-4d87-a550-04c97fbb9231/download

I have double check that there is no href attribute in that button tag. So I have use a plugin called clipboardy and I have added plugins/index.js file

 const clipboardy = require('clipboardy');
    module.exports = ( on ) => {
        on('task', {
            getClipboard () {
                return clipboardy.readSync();
            }
        });
    };

In my test I have used cy.task() to get the clipboard content, but this is not printing the actual url content

cy.get('td').find('div').find('span').find('button').find('i').eq(0).click().then(()=>{
          cy.task('getClipboard').then((data)=>{
          console.log("Helloooo:"+data);
      })
    })

<td class="sc-hgRTRy duUdhJ">
<div>
<span class="sc-bYwvMP jTmLTC">
<span class="sc-jzJRlG iVpVVy">
<span role="button" aria-expanded="true" aria-haspopup="true" aria-owns="5aa03785-1370-455e-a838-4154f7481a7b">
<button class="sc-feJyhm cJOKrG">
<i class="icon fas fa-link sc-htpNat cQABgO" aria-hidden="true" data-component-type="icon">
</i>
</button>
</span>
</span>
</span>
</div>
</td>

enter image description here

soccerway
  • 10,371
  • 19
  • 67
  • 132
  • I have tried `getClipboard () { return clipboardy.read(); }` as well – soccerway May 07 '20 at 07:11
  • 1
    The test given by @MakwanaPrahlad (using an input) works, so that suggests that your button `.click()` is not triggering the event. That may be the 'native events' problem, but it's also possible the click is not on the right element. Can you show part of the html that has the `td`? – Richard Matsen May 07 '20 at 23:29
  • It would also be helpful to know if the 'copy to clipboard' message appears when running the test. – Richard Matsen May 07 '20 at 23:35
  • Yes it is displaying the message while running the text. I totally agree to @MakwanaPrahlad test, but my case is different though – soccerway May 08 '20 at 01:52
  • I will add the html.. – soccerway May 08 '20 at 01:52
  • Added the html tag – soccerway May 08 '20 at 02:01
  • Thanks for the update. I think the point about @MakwanaPrahlad's test is that it proves your approach to checking the clipboard content is valid. It also means that there's nothing in the clipboard within your `.then()`. You could try a `wait()` before the `cy.task()`, in case the copy-to-clipboard process is slow (your test will run clipboardy ms after the click). – Richard Matsen May 08 '20 at 02:17
  • I have tried waiting for 5 seconds. But still not good. I have also confirmed if the clipboard content is still available by manually press Ctrl V after the message in a new tab in the cypress browser and paste it there..I could see content there.. – soccerway May 08 '20 at 02:37
  • OK. What does `console.log(data)` look like? Undefined, or a jquery object? – Richard Matsen May 08 '20 at 04:39
  • It just print the whole cy.task() function.. – soccerway May 08 '20 at 04:57
  • 3
    I don't understand, can you post it on the question? Normally the parameter of `.then()` is a jquery object, so you probably can't do `"Hello:" + data`, instead maybe `"Hello:" + data[0]`. – Richard Matsen May 08 '20 at 05:33
  • @soccerway I am currently facing the same issue. Did you manage to solve this? I think the index.js file is run before the tests and then whatever content the clipboard had before it will just use that and not the latest content. – Alapan Das Aug 12 '20 at 07:17
  • @AlapanDas Sorry it didn't work for me. – soccerway Aug 14 '20 at 03:05

12 Answers12

16

If you don't want to use clipboardy package you can also use this:

cy.window().then((win) => {
    win.navigator.clipboard.readText().then((text) => {
      expect(text).to.eq('your copied text');
    });
  });

in your case, it turns to something like

   cy.get('td').find('div').find('span').find('button').find('i').eq(0).click().then(()=>{
    cy.window().then((win) => {
      win.navigator.clipboard.readText().then((text) => {
        console.log("Helloooo:"+text);
      });
    });
   })
marzzy
  • 706
  • 10
  • 21
  • 11
    I keep getting the exception "Document is not focused" – BrunoElo Oct 07 '21 at 05:38
  • 3
    Make sure that you haven't clicked away to another window or something. I've gotten the same error, and it was literally just that the browser that is being tested had to be the active window. – Tyler Nov 17 '21 at 14:17
  • What syntax do you use to check for text value within then() ? expect( ) what ? – trainoasis Dec 10 '21 at 10:26
8

Accessing clipboard can be worked around, but the main issue is that the document.execCommand('copy') doesn't work (as stated above), which I reckon is the primary (and only?) way for your app to programmatically put a text to user's clipboard.

Assuming it happens somehow (or is fixed upstream), the checking the clipboard's contents can be done e.g. by using clipboardy:

npm i -D clipboardy

plugins/index.js:

const clipboardy = require('clipboardy');
module.exports = ( on ) => {
    on('task', {
        getClipboard () {
            return clipboardy.readSync();
        }
    });
};

In your specification:

describe('test', () => {
    it('test', () => {
        cy.document().then( doc => {
            doc.body.innerHTML = '<input id="inp">';
        });
        cy.get('#inp').type('test{selectall}');
        cy.document().then( doc => {
            doc.execCommand('copy');
        });
        cy.task('getClipboard').should('contain', 'test');
    });
});

I hope this code will be usefull for you. Thank you.

Reza
  • 18,865
  • 13
  • 88
  • 163
Makwana Prahlad
  • 1,025
  • 5
  • 20
  • Thanks, I have see the same approach earlier, in your example you have an input field, typing some text into that input field and then asserting that text content. My requirement is different, there is no input field in my web application, all I have is a button, when i click on the above button, some text content is copied into clipboard, then i will get a pop up message "Copied to Clipboard", I need to grab that clipboard content ( see my question above). I need to grab that url to proceed further with rest of business cases. – soccerway May 07 '20 at 09:43
  • I have inspected and find there is no `href` attribute in that button ( as given in above screen shot in question ) – soccerway May 07 '20 at 09:46
  • I have tried the below one too, but it just print the code, `cy.get('td').find('div').find('span').find('button').find('i').eq(0).click().task('getClipboard').then((data)=>{ console.log("Helloooo:"+data); })` – soccerway May 07 '20 at 09:49
  • The readme page on github for clipboardy says that readSync() doesn't work in browsers. I'm also seeing intermittent issues where it causes the cypress test run to hang. Are other folks not seeing these issues? – opike May 05 '21 at 21:55
7

I have found this nice and simple solution of testing the clipboard content in Gleb Bahmutov's video Access the clipboard from Cypress test using Electron browser.

cy.window().its('navigator.clipboard')
  .invoke('readText').should('equal', 'copied text')

It uses the new Clipboard API (Chrome, FF and Opera: 2018; Safari and Edge: 2020; IE: no)

In the video, Gleb also suggets to test a case for browsers not supporting the Clipboard API, using an old (and deprecated) method of document.execCommand('copy').

It expects the page to have an input field or textarea to paste the content. Let's assume it's textarea#paste-here.

// delete navigator.clipboard to force the app to use execCommand
cy.visit('index.html', {
  onBeforeLoad(window) {
    delete window.navigator.__proto__.clipboard
  }
})
cy.document().then(document => cy.spy(document, 'execCommand').as('exec'))

/* trigger the copy action here... cy.get(...).click() */

cy.get('@exec').should('have.been.calledOnceWith', 'copy')

cy.get('#paste-here').focus()
cy.document().invoke('execCommand', 'paste')

cy.get('#paste-here').should('have.value', 'copied text')

Due to the clipboard permissions dialog, this test can be automated (CI) only in Electron browser. To make it running in Chrome, take a look at another Gleb's video Give Chrome Browser Clipboard Permissions from Cypress Test. Otherwise, you can limit the test to be run only in Electron using Test configuration object:

describe('Clipboard', {browser: 'electron'}, () => {
...
  • Interesting, my specs are "skipped due to browser" when electron is set – trainoasis Dec 10 '21 at 09:56
  • 2
    Also, cy.window().its('navigator.clipboard') .invoke('readText').should('equal', 'copied text') throws "TypeError": Cannot set property message of [object DOMException] which has only a getter all of a sudden now .. – trainoasis Dec 10 '21 at 10:21
5

Late but if anyone is getting "Document is not focused" error from @marzzy's answer:

cy.window().then((win) => {
    win.navigator.clipboard.readText().then((text) => {
      expect(text).to.eq('your copied text');
    });
  });

It's either because:

  1. You are executing code from the devtools console or snippets

  2. Or if you're using Chrome you have to give it clipboard permission like this:

cy.wrap(
    Cypress.automation('remote:debugger:protocol', {
        command: 'Browser.grantPermissions',
        params: {
          permissions: ['clipboardReadWrite', 'clipboardSanitizedWrite'],
          origin: window.location.origin,
        },
    }),
);

Source: https://www.youtube.com/watch?v=4eEc3x24D64

jeffng50
  • 199
  • 3
  • 15
2

For me, none of the answers helped me taking de data from the clipboard. The next code worked:

First we will allow the access to the clipboard:

cy.window().its('navigator.permissions')
                .invoke('query', { name: 'clipboard-read' })
                .its('state').then(cy.log)

Then we access the clipboard

cy.window().its('navigator.clipboard')
                .invoke('readText')
                .then((text) => {
                    cy.log(text)
                })

Hope this works for anyone here

SrArkaitz
  • 488
  • 2
  • 8
  • 20
1

I managed to get the copied link by printing out the yielded value after clicking the button and then searched the object in the console in order to find the link. I found the link in [object]._vClipboard.clipboardAction.text, as you can see below:

cy.get('[data-test=copy-link-button]').click().then(($clipboard) => {
   const copiedLink = $clipboard[0]._vClipboard.clipboardAction.text;
   cy.visit(copiedLink);
});

I hope that this will help.

Realdo Dias
  • 517
  • 5
  • 11
  • How did you print out the yielded value? My button has text of "Copy Text", so i can't grab it's own text, i need to grab what it is placing in the clipboard. So, i'm wondering how did you print the yielded value after clicking the button? – Daniel Aug 06 '20 at 22:33
  • 1
    @SaintAvalon, you can do this: `cy.get('[data-test=copy-link-button]').click().then(($value) => { console.log($value); });` This will print to the console the yielded value after clicking the button. Then you can see the clipboard text in the object printed out in the console: `$value[0]._vClipboard.clipboardAction.text`. Make sure you replace `[data-test=copy-link-button]` with your respective button's data-test – Realdo Dias Aug 07 '20 at 07:02
1

Reading this really helped me solve my own issue, I had to use a mixture from the OP and the first comment. This is what worked for me:

     cy.get(':nth-child(2) > .mat-card > .row > .col-sm-3 > .mat-focus-indicator').click().then(() => {
        cy.task('getClipboard').then(($clip) => {
             const url = $clip;
             cy.log('this is what was in clipboard', url);
             cy.visit(url);
         });
     });

This gave me the URL i wanted from the button I clicked. I then just passed that right into cy.visit to visit the url i just copied. Worked great.

Daniel
  • 95
  • 1
  • 11
0

Code at https://stackoverflow.com/a/61653634/2742007 should be adapted to call clipboardy functions supported by browsers, as commented by "opike": mind the async and read() instead of readSync().

const clipboardy = require('clipboardy');
module.exports = (on, config) => {
  on('task', {
    async getClipboard() {
      return clipboardy.read();
    }
  });
}
Fred Klein
  • 448
  • 6
  • 13
0

Note: Above usage of window.navigator https://stackoverflow.com/a/68871590/5667941 , only works for Electron. For Chrome user permisson would be required

User permission request

doktordirk
  • 21
  • 1
0

Try using cy.window() and you can use the navigator.clipboard as normal for pasting and copying.

it("Paste cliboard numbers", () => {
  cy.window()
    .then((win) => {
      return win.navigator.clipboard.writeText("234");
    `})`
    .then(() => {
      cy.get("[data-test=paste]").click();
      cy.get("[data-test=calc-input]").should("have.text", 234);
    });
});
Weam Adel
  • 151
  • 1
  • 11
0

None of the other answares worked for me so far with the cypress ui but I managedto find a solution without any additional libs or plugins by using the Cypress.Promises and the window.navigator directly ... and here it is:

cy.window().then(win =>
  new Cypress.Promise((resolve, reject) =>
    win.navigator
      .clipboard
      .readText()
      .then(resolve)
      .catch(reject))
).should('eq', '`myExpectedClipboardValue')
Mariano
  • 478
  • 4
  • 9
0

You have to give clipboard read permission.Befor click button to have copied text add this.

cy.wrap(Cypress.automation("remote:debugger:protocol", {
command: "Browser.grantPermissions",
params: {
    permissions: ["clipboardReadWrite", "clipboardSanitizedWrite"],
    // make the permission tighter by allowing the current origin only
    origin: window.location.origin
}

}));