Looking for a way to excute a command line from cypress - 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!

Related

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.

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)
})
})
}
})

Cypress: How do I conditionally skip a test by checking the URL

How do I conditionally skip a test if the URL contains "xyz"?
some tests that run in the QA environment "abc" should not be run in Production "xyz" environment.
I've not been able to find a good example of conditionally checking for environment to trigger a test. The baseURL needs to be checked dynamically and the test skipped preferably in the beforeEach.
running cypress 6.2.0
beforeEach(() => {
login.loginByUser('TomJones');
cy.visit(`${environment.getBaseUrl()}${route}`);
});
it('test page', function () {
if environment.getBaseUrl().contains("xyz")
then *skip test*
else
cy.intercept('GET', '**/some-api/v1/test*').as('Test'););
cy.get('#submitButton').click();
})
Potential Solution (tested and tried successfully):
I used a combination of filtering (grouping) and folder structures via CLI
I set folders /integrations/smokeTest/QA and /integrations/smokeTest/Prod/
1.QA Test Run:
npm run *cy:filter:qa* --spec "cypresss/integration/smokeTests/QA/*-spec.ts"
2.Run All (both QA and PROD tests)
npm run cypress:open --spec "cypresss/integration/smokeTests/*/*-spec.ts"
3. Prod Test Run:
npm run cy:filter:prod --spec "cypresss/integration/smokeTests/PROD*/*-spec.ts"
Normally I wouldn't write a custom command just to exercise one Cypress command, but in this case it's useful to obtain the global test context this.
Using the function form of callback with the custom command allows access to this, then you are free to use arrow functions on the test themselves.
Cypress.Commands.add('skipWhen', function (expression) {
if (expression) {
this.skip()
}
})
it('test skipping with arrow function', () => {
cy.skipWhen(Cypress.config('baseUrl').includes('localhost'));
// NOTE: a "naked" expect() will not be skipped
// if you call your custom command within the test
// Wrap it in a .then() to make sure it executes on the command queue
cy.then(() => {
expect('this.stackOverflow.answer').to.eq('a.better.example')
})
})
I would add a helper method which you can call from any Mocha.Context (at the time of writing, any it, describe, or context block).
// commands.ts
declare global {
// eslint-disable-next-line #typescript-eslint/no-namespace
namespace Cypress {
interface Chainable {
/**
* Custom command which will skip a test or context based on a boolean expression.
*
* You can call this command from anywhere, just make sure to pass in the the it, describe, or context block you wish to skip.
*
* #example cy.skipIf(yourCondition, this);
*/
skipIf(expression: boolean, context: Mocha.Context): void;
}
}
}
Cypress.Commands.add(
'skipIf',
(expression: boolean, context: Mocha.Context) => {
if (expression) context.skip.bind(context)();
}
);
And from your spec:
describe('Events', () => {
const url = `${environment.getBaseUrl()}${route}`;
before(function () {
cy.visit(url);
cy.skipIf(url.includes('xyz'), this);
});
context('Nested context', () => {
it('test', function () {
cy.skipIf(url.includes('abc'), this);
expect(this.stackOverflow.answer).to.be('accepted');
});
});
});
Now you have a reusable custom command that you can call from anywhere to conditionally skip tests based on any expression that evaluates to a boolean. Careful of classic JS equality and definition gotchas (read more about equality in JS here).

How can I run a shell script as a setup file in Jest?

Something like this. The goal is to start and stop my test server as part of the jest setup so I can do end to end testing with a single command.
"jest": {
"setupFiles": ["<rootDir>/TestScript.sh"]
}
I will answer this myself for anyone in the future who is trying to solve the same problem as me.
Jest config has globalSetup and globalTeardown options. This script will be run once at the beginning of all tests.
"jest": {
"globalSetup": "<rootDir>/jestGlobalSetup.js"
}
In node, you can use the child_process API to run shell scripts from a js file.
I ran mine like this in my setup file.
import { spawn } from 'child_process';
import cwd from 'cwd';
export default async function setup() {
process.stdout.write('Starting Server');
// Run this command in shell.
// Every argument needs to be a separate string in an an array.
const command = 'foreman';
const arguments = [
'start',
'-p',
'3000',
'-f',
'Procfile.test',
];
const options = {
shell: true,
cwd: cwd()
};
const server = spawn(
command,
arguments,
options,
);
// Then I run a custom script that pings the server until it returns a 200.
await serverReady();
}
you have to configure the globalSetup option in your jest.config.js file
module.exports = {
...
globalSetup: "<rootDir>/jestGlobalSetup.js",
...
};
then create the jestGlobalSetup.js file as following:
const exec = require('child_process').exec;
module.exports = async function() {
const myShellScript = exec('./TestScript.sh');//or run any other shell command
myShellScript.stdout.on('data', (data)=>{
console.log(data);
// do whatever you want here with data
});
myShellScript.stderr.on('data', (data)=>{
console.error(data);
})
}

grunt, grunt-shell and command argument

I would like to pass an argument to grunt-shell like it is defined in the documentation:
module.exports = function(grunt) {
// Configure Grunt
grunt.initConfig({
shell: {
hello: {
command: function (greeting) {
return 'echo ' + greeting;
},
options: {
stdout: true
}
}
}
});
grunt.loadNpmTasks('grunt-shell');
grunt.registerTask('d', 'shell:hello');
When I execute it without argument it's working but when I try to put an argument I got an error:
Julio:Server julio$ grunt d
Running "shell:hello" (shell) task
undefined
Done, without errors.
Julio:Server julio$ grunt d:me
Warning: Task "me" not found. Use --force to continue.
Aborted due to warnings.
Where is my misunderstanding?
Thank you
Your problem is the alias, aliases don't work like you think.
If you use
grunt shell:hello:me
Then it'll work the way you expect it to.
Since aliases can be a list of zero or more tasks, it wouldn't make sense for them to pass parameters to other classes. If you want to alias it so badly, then the best you can hope for is creating another task to do the aliasing, rather than a real alias.
grunt.registerTask('d', function (greeting) {
grunt.task.run('shell:hello:' + greeting);
});
In that case then you would be able to do what you intended, using
grunt d:me

Resources