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>
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);
});
});
})
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.
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'}, () => {
...
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:
You are executing code from the devtools console or snippets
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
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.
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.
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
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();
}
});
}
Note: Above usage of window.navigator https://stackoverflow.com/a/68871590/5667941 , only works for Electron. For Chrome user permisson would be required
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);
});
});
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')
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
}
}));
Related
I have a web page with some JavaScript code that copies stuff to the clipboard similar to what this demo does: https://davidwalsh.name/demo/javascript-copy-clipboard.php
My code is something like this:
// Within a listener that is triggered by a click on some button:
var copiedText = "...something";
navigator.clipboard.writeText(copiedText).then(
function() {
console.log("Succesfully copied");
},
function() {
console.log("FAILED to copy!!!!!!");
}
);
This works fine, but it always succeeds. I need to test that the behavior is correct when copying fails for whatever reason.
How can I cause the copy to fail on purpose so that I can test the behavior of my code in that situation?
I never get a prompt asking me permission to write to the clipboard.
In Chrome, I have tried going to the site settings for the site, and under "Clipboard" selecting "Block", but it does nothing (I guess that's only for reading from the clipboard).
Sure, just provide an invalid argument which will throw an exception during the stringification algorithm:
Note that this demo will work in your own page, but the success case won't work in the Stack Overflow code snippet iframe sandbox (where there is no clipboard permission).
function copyThenLog (input) {
navigator.clipboard.writeText(input).then(
() => console.log({success: true}),
(ex) => {
console.log({success: false});
console.error(ex);
},
);
}
document.querySelector('.success').addEventListener('click', () => {
const input = 'hello world';
copyThenLog(input);
});
document.querySelector('.fail').addEventListener('click', () => {
const input = {
toString () {
throw new Error('Oops');
}
};
copyThenLog(input);
});
<div class="success">Click me to succeed</div>
<div class="fail">Click me to fail</div>
I am pretty new at coding in Javascript. I am trying to Copy my url through the action of button click which will fire the function below CopyTextFunction(). But I am not being able to find out why it is not working since alert(copyText); in my function shows that the text was copied.
function CopyTextFunction() {
const params = new URLSearchParams(window.location.search);
params.get('ID')
var copyText = "http://randomsite.com/OnlineShop/ShopProducts?ID=" + params.get('ID');
//alert(copyText);
alert(copyText);
navigator.clipboard
.writeText(copyText)
.then(() => {
alert("successfully copied");
})
.catch(() => {
alert("something went wrong");
});
}
May you should check Browser compatibility for clipboard.writeText. I mean... writing permissions for Clipboard depends on browser versions and contexts. Please, have a look to * marks in the link:
https://developer.mozilla.org/en-US/docs/Web/API/Clipboard_API#browser_compatibility
Hi I am working with cypress for UI testing. Based on user we do have additional button on the page.
Is there a way cypress can look for a button and run the commands if button is present and skip the command if button is absent thus preventing from element not found error.
<div class="btn btn-success" id="editButton">Edit</div>
Cypress code is
if (!cy.get('div[id=editButton]')) {
this.skip();
} else {
cy.get('div[id=editButton]').click();
}
And yet cypress throws element not found error.
Perhaps use a variation of Dynamic Text test
cy.get('body').then(($body) => {
const button = $body.find('div[id=editButton]')
if (button.length) {
// yup found it
cy.get('div[id=editButton]').click()
}
// don't need this.skip()
})
Please see the caveats on that page about conditional testing. If your $body.find('div[id=editButton]') fails because the button has not appeared yet, you will need to add more assertions to the test.
Another approach is to test for different classes of user. This makes your test suite more complete
For example,
it('tests the admin user', () => {
cy.login('admin')
cy.get('div[id=editButton]').click()
...
callCommonUserTests()
})
it('tests the read-only user', () => {
cy.login('read-only')
// cy.get('div[id=editButton]').click() // edit button not available
...
callCommonUserTests()
})
Now the test suite is much simpler and less prone to timing issues.
You can consolidate code common for all users in functions or custom commands.
To use jQuery with Cypress.$ you can test the existence without failing the test
it('tests the edit button', () => {
if (Cypress.$('#editButton').length) {
cy.get('div[id=editButton]').click()
// rest of test
}
})
Or
it('tests the edit button', () => {
if (!Cypress.$('#editButton').length) {
this.skip() // exit the test here
}
cy.get('div[id=editButton]').click()
// rest of test
})
I would go with the below option.
cy.get('div[id="editButton"]')
.then (($element) => {
if($element.length) cy.get('div[id=editButton]').click()
})
Instead of using the cypress command you have to use a JQuery command for this. And in that you have to check the length. If its 0, then the element doesn't exist.
if (Cypress.$('div[id=editButton]').length == 0) {
this.skip()
} else {
cy.get('div[id=editButton]').click()
}
You can check if the Edit button is on the page without failing the test
cy.get('div.btn').then($buttons => {
if ($buttons.text().includes('Edit')) {
cy.get('div[id=editButton]').click()
})
})
Ive enabled deep linking and everything works great when the application opens. When I open the app from a closed state using the url moderatorapp://hello it logs the correct url, but it does not work when the app is deep linked while being opened from a background state. My code is as follows:
componentDidMount() {
// Storage.clear();
Storage.getItem('data_moderator')
.then(_data => {
if (_data && _data.tokens) {
this.autoLogin(_data.tokens);
} else {
Actions.loginForm();
}
}
);
Linking.getInitialURL()
.then(url => {
console.log('Initial Url then ', url);
if (url) {
console.log('Initial Url ', url);
}
})
.catch(error => console.log(error));
Linking.addEventListener('url', this.handleOpenURL);
}
This is obviously because the componentDidMount method is not being called at that point.
What I have tried:
I attempted to wrap the Linking code inside of an event that detects the application entering into the active state and it doesn't work, it logs the same url from the initial attempt when the app was closed. When I attempt to deep link into the app from the background state using the url moderatorapp://goodbye it logs the moderatorapp://hello. So it somehow is not updating.
AppState.addEventListener('change', (state) => {
if (state === 'active') {
console.log('state active');
Linking.getInitialURL()
.then(url => {
console.log('Initial Url then ', url);
if (url) {
console.log('Initial Url ', url);
}
})
.catch(error => console.log(error));
}
if(state === 'background'){
console.log('background');
}
});
Im really new to React Native, any assistance would be greatly appreciated.
Thanks.
https://facebook.github.io/react-native/docs/linking.html Specifically:
- (BOOL)application:(UIApplication *)application
openURL:(NSURL *)url
options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options
{
return [RCTLinkingManager application:application openURL:url options:options];
}
Apple changed the api for linking so if you are targeting ios 9 or newer, you need this code in your AppDelegate.m file.
The deep linking is working as expected for me even the app is in background. Please check the below specifications.
Node Version : v12.18.x OR Greater
NPM Version : v6.14.x OR Greater
react-native-cli : 2.0.1
react-native : 0.63.x OR Greater
Please check if you have added below line in your AppDelegate.m.
#import <React/RCTLinkingManager.h>
It must be added above #ifdef FB_SONARKIT_ENABLED line. Adding it below this line will cause failing of build while Archiving it for release.
Please check if you have added below code in your AppDelegate.m which is responsible for deep linking.
- (BOOL)application:(UIApplication *)application
openURL:(NSURL *)url
options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options
{
return [RCTLinkingManager application:application openURL:url options:options];
}
It will work for app cold boot, but it will not work if your app is in background. For this, you need to add below code in your AppDelegate.m
- (BOOL)application:(UIApplication *)application continueUserActivity:(NSUserActivity *)userActivity restorationHandler:(nonnull void (^)(NSArray<id> *_Nullable))restorationHandler {
return [RCTLinkingManager application:application
continueUserActivity:userActivity
restorationHandler:restorationHandler];
}
This should work irrespective of your AppState: active **OR** background.
This worked for me as expected! Give it a try. This is should definitely work.
Thanks in advance!
I am using twilio API to implement screen sharing in an emberjs app, I am successfully able to share the screen and also toggle on stopping it. Here is my code ->
this.get('detectRtc').isChromeExtensionAvailable(available => {
if (available) {
const { twilioParticipant } = this.get('participant')
if (this.get('stream') && this.get('stream').active) {
this.get('streamTrack').stop()
this.get('userMedia.mediaStream')
.removeTrack(this.get('streamTrack'))
this.set('isEnabled', false)
twilioParticipant.removeTrack(this.get('streamTrack'))
} else {
this.get('detectRtc').getSourceId(sourceId => {
// "cancel" button is clicked
if (sourceId !== 'PermissionDeniedError') {
// "share" button is clicked extension returns sourceId
this.get('userMedia')
.getScreen(sourceId)
.then(mediaStream => {
this.set('isEnabled', true)
this.set('stream', mediaStream)
this.set('streamTrack', mediaStream.getVideoTracks()[0])
twilioParticipant.addTrack(mediaStream.getVideoTracks()[0])
})
.catch(() => { /* do nothing, but return something */ })
}
})
}
} else {
this.get('flash').status(
'base',
this.get('intl').t('chromeExtension.install'),
{
icon: 'alert-circle',
push: true
}
)
// TODO Show the system popup to install chrome extension from web store
// !!chrome.webstore &&
// !!chrome.webstore.install &&
// chrome.webstore.install(this.webStoreUrl)
}
})
The issue I'm facing is with the stop sharing button which is at the bottom of the app as seen in screenshot below
I need a way to listen to an event handler and execute the some code after clicking on the stop sharing screen button, I know there is an onended event Handler which is mentioned in the MediaStreamTrack docs, but I don't know how to use it, any help will be highly appreciated.
https://developer.mozilla.org/en-US/docs/Web/API/MediaStreamTrack
The "stop sharing" button will trigger the MediaStreamTracks 'ended' event. Try this:
mediaStream.getVideoTracks()[0].addEventListener('ended', () => console.log('screensharing has ended'))
for some reason, #philipp answer is not working for me and I found this quite helpful
https://github.com/webrtc/samples/blob/gh-pages/src/content/getusermedia/getdisplaymedia/js/main.js#L88
this.get('stream').addEventListener('inactive', e => {
console.log('Capture stream inactive - stop recording!');
});