Log as error message in the cypress command log - cypress

With log it is possible to log a message to the cypress command log. This has a blue color and is for information.
Is it also possible to log an error message?
The use-case is this: I have added a command. But for this command to successfully execute the environment variables have to be set. I would like the task to pause when no environment variable has been set, and the refer to the docs:
Cypress.Commands.add('login', () => {
if (!Cypress.env('SOME_API_URL')) {
// ideally this would be a red message instead of blue
cy.log('Can\'t login because not all necessary Cypress environment variables have been set.');
cy.log('Please check the readme in this project.');
cy.pause();
}
cy.request({
url: Cypress.env('SOME_API_URL'),
// left out for brevity
}).then(resp => {
// left out for brevity
cy.log('succesfully logged in.');
}).as('login');
}

Related

Looking for a way to excute a command line from cypress

I need to create a file and copy it somewhere by some code from cypress .
the first step is done by using cy.writeFile and now myfile.txt is created
Now i need to copy it somewhere like c:/lib/Sth
i used this command cy.exec('cp myfile.txt c:/lib/sth')
it shows this error message :
CypressError: cy.exec('cp myfile.txt c:/lib/sth') failed because the command exited with a non-zero code. Pass {failOnNonZeroExit: false}` to ignore exit code failures.
Information about the failure:
Code: 127
I add {failOnNonZeroExit: false} to my code to ignore error , it works , but my file is not copied.
is there any other solution to copy my file from cypress ??
A work-around you could do is set up a custom cypress task to execute a command.
Something like
// cypress/plugins/index.ts
const { exec } = require('child_process');
/**
* #type {Cypress.PluginConfig}
*/
// eslint-disable-next-line no-unused-vars
module.exports = (on, config) => {
// `on` is used to hook into various events Cypress emits
// `config` is the resolved Cypress config
on('task', {
async execute(command: string) {
return new Promise((resolve, reject) => {
try {
resolve(exec(command));
} catch (e) {
reject(e);
}
});
},
});
};
Then execute like so
cy.task('execute', 'cp myfile.txt c:/lib/sth');
This was a potential solution I came up with when cy.exec() didn't work for me either when trying to execute a relatively complex node script.
Another thing you could try is to create a really simple script that copies the file, and try executing that script.
Best of luck!

In my Cypress.io tests why do I need to treat a cy.task like it is asyncronous when its not

I have Cypress.io tests that use a simple Cypress task to log some table information to the terminal. For example I have a test like so:
it("Writes some console logs and checks a val", () => {
cy.task("rowLog", { id: 1, name: "foo", type: "bar" });
let one = 1;
expect(one).to.equal(2);
});
And the task, "rowLog" like so:
module.exports = (on, config) => {
on("task", {
rowLog(data) {
// use node.js console.table to pretty print table data:
console.table(data);
return null;
},
}
But the result of rowLog will not display in my terminal if I run Cypress headlessly via Cypress run. This is because the test will fail. If we switch the test so that it passes, then it will show.
However I just realized that if I treat rowLog like it's async like below. It will print the results to the terminal:
it("Writes some console logs and checks a val", () => {
// add a .then to task:
cy.task("rowLog", { id: 1, name: "foo", type: "bar" }).then(() => {
let one = 1;
expect(one).to.equal(2);
});
});
This is not what I would expect from the docs. They say that:
cy.task() yields the value returned or resolved by the task event in the pluginsFile.
(source)
And that a task can yield either a promise or a value.
I'm new to Cypress here-- is there something I'm missing or doing wrong? I'd like to be able to not have to chain my tasks with .then statements if it is just synchronous stuff like writing output to ensure everything is emitted to my terminal.
If you look into the type definition of cy.task command, you will see that it returns a Chainable (that is a promise-like entity). So it behaves like any other cy command (ansynchrounously).
As for the yield either a promise or a **value** - this statement refers to the handler of the task, not the task itself. As for the other command, Cypress will wrap a returned value into a promise if it was not done by the handler.

How to run the same test with multiple logins, URL's, and body elements in cypress.io

I have a simple test I want to create in cypress that would require a test where using a settings file I would create 1 test that executes for each entry in the settings file. The file would contain user/pwd/url/elementID and be used to login for each user at a custom URL, and validate that a specific elementID is displayed, logout, and do it again - iterating through the settings file until each is tested.
I want to do something like:
forEach(URL,uname,pwd,elementID) do
cy.request(URL)
cy.get('input:uname').btn.click
cy.get('input:pwd').btn.click
cy.get(data-cy=elementID).should(be present)
cy.get(btn.logout).btn.click
I highly doubt the above code is correct - but hopefully you get the idea. Main goal is to create a simple and quick script that will quickly iterate through an array to smoke test the functionality.
You can still iterate over your test data and create a test case out of each:
[
{
url,
uname,
pwd,
elementID,
}
].forEach(testData => {
it(`Test ${testData.uname} on ${testData.url}`, () => {
// your test code
});
});
Of course the array:
[
{
url,
uname,
pwd,
elementID,
}
]
does not need to be there in the same file, you can have it somewhere separate and import it into your spec file.
Caveat: You can only visit URLs from the same origin in one test! This code will only work if all URLs you want to test are from the same origin (i.e. same
Save your data in json format and put them in Cypress folder "fixtures"
[
{"user":"username1","pwd":"pwuser1","url":"url1","elementID":"#element_name1"},
{"user":"username2","pwd":"pwuser2","url":"url2","elementID":"#element_name2"}
]
(Don't forget the # in front of the element_name id)
Then this is your smoke_test.spec.js
//fetch the parameters from the file and save them as constant "login"
const login_data = require('../fixtures/login_data.json')
//Now you can fetch the parameters using "login_data"
describe('smoke test', () => {
it('loop through login list', () => {
//we call each entry "param" and loop through the lines of the json file
cy.get(login_data).each((param) => {
cy.visit(param.url)
cy.get('#id_of_username_field').type(param.user)
cy.get('#id_of_pw_field').type(param.pwd)
//The next line is only if you have a login button
cy.get('#id_of_login_button').click()
cy.get(param.elementID).should('be.visible')
cy.get('#id_of_logout_button)
})
})
})

systematic failure when trying to execute a system command with Cypress

I'm new to Cypress and Javascript
I'm trying to send system commands through Cypress. I've been through several examples but even the simplest does not work.
it always fails with the following message
Information about the failure:
Code: 127
Stderr:
/c/Program: Files\Git\usr\bin\bash.exe: No such file or directory`
I'm trying cy.exec('pwd') or 'ls' to see where it is launched from but it does not work.
Is there a particular include I am missing ? some particular configuration ?
EDIT :
indeed, I'm not clear about the context I'm trying to use the command in. However, I don't set any path explicitely.
I send requests on a linux server but I also would like to send system commands.
My cypress project is in /c/Cypress/test_integration/cypress
I work with a .feature file located in /c/Cypress/test_integration/cypress/features/System and my scenario calls a function in a file system.js located in /c/Cypress/test_integration/cypress/step_definitions/generic.
System_operations.features:
Scenario: [0004] - Restore HBox configuration
Given I am logging with "Administrator" account from API
And I store the actual configuration
...
Then I my .js file, I want to send a system command
system.js:
Given('I store the actual configuration', () => {
let nb_elem = 0
cy.exec('ls -l')
...
})
I did no particular path configuration in VS Code for the use of bash command (I just configured the terminal in bash instead of powershell)
Finally, with some help, I managed to call system functions by using tasks.
In my function I call :
cy.task('send_system_cmd', 'pwd').then((output) => {
console.log("output = ", output)
})
with a task created as follows:
on('task', {
send_system_cmd(cmd) {
console.log("task test command system")
const execSync = require('child_process').execSync;
const output = execSync(cmd, { encoding: 'utf-8' });
return output
}
})
this works at least for simple commands, I haven't tried much further for the moment.
UPDATE for LINUX system commands as the previous method works for WINDOWS
(sorry, I can't remember where I found this method, it's not my credit. though it fulfills my needs)
This case requires node-ssh
Still using tasks, the function call is done like this
cy.task('send_system_cmd', {cmd:"<my_command>", endpoint:<address>,user:<ssh_login>, pwd:<ssh_password>}).then((output) => {
<process output.stdout or output.stderr>
})
with the task being build like this:
// send system command - remote
on('task', {
send_system_cmd({cmd, endpoint, user, pwd}) {
return new Promise((resolve, reject) => {
const { NodeSSH } = require('node-ssh')
const ssh = new NodeSSH()
let ssh_output = {}
ssh.connect({
host: endpoint,
username: user,
password: pwd
})
.then(() => {
if(!ssh.isConnected())
reject("ssh connection not set")
//console.log("ssh connection OK, send command")
ssh.execCommand(cmd).then(function (result) {
ssh_output["stderr"] = result.stderr
ssh_output["stdout"] = result.stdout
resolve(ssh_output)
});
})
.catch((err)=>{
console.log(err)
reject(err)
})
})
}
})

Directory path is incorrect when running from cypress test runner

When I login from normal browser the login is successful with the URL : http://neelesh.zapto.org:8084/EnrolMe/indHome.html
But when I run the script from Cypress the directory location is not appended and the new URL after login is formed as : http://neelesh.zapto.org:8084/__/indHome.html
I have tried setting cypress.json with
{
"chromeWebSecurity": false,
"modifyObstructiveCode" : false
}
I have tried on chrome/electron(head and headless).
Below is my code snippet:
describe('My First Test Suite', function() {
it('My First test case', function() {
cy.visit("http://neelesh.zapto.org:8084/EnrolMe")
cy.get("#login").click()
cy.get("input[value='Individual']").click()
cy.get("#username").type('1234567890')
cy.get("#pwd").type('0646')
Cypress.Cookies.debug(true)
cy.clearCookies()
cy.get("#login").click()
cy.wait(6000)
})
})
When I run the script from Cypress the directory location is not appended and the new URL after login is formed as : http://neelesh.zapto.org:8084/__/indHome.html
It should be redirected as : http://neelesh.zapto.org:8084/EnrolMe/indHome.html
Can anyone help me on this?
This sounds like an issue with "Frame Busting". There's a related discussion for Cypress GitHub Issue #992 which may lend some help.
Your application code may contain problematic frame busting code like the following:
if (window.top !== window.self) {
window.top.location.href = window.self.location.href;
}
You can get around this by changing your application code's reference to window.self from the Application Window to the Cypress Test Runner window (window.top).
Cypress emits a series of events as it runs in your browser. You can use the emitted window:before:load application event to ensure it's done before you attempt to login.
// cypress/support/index.js
Cypress.on('window:before:load', (win) => {
Object.defineProperty(win, 'self', {
get: () => {
return window.top
}
})
})

Resources