how do I run a shell script from a test (jest)? I tried this:
var cp = require("child_process");
test("script", async () => {
const script = cp.exec("./scripts/foo.sh");
script.stdout.on("data", (data) => {
console.log("stdout: " data);
});
script.stderr.on("data", (data) => {
console.error("stderr: " data);
});
});
where foo.sh:
#!/usr/bin/env bash
echo "fooooo"
and that gives:
Cannot log after tests are done. Did you forget to wait for something async in your test?
Attempted to log "stdout: fooooo"
Also, if I remove that it still doesn't show the console.log outputs
Any ideas?
Edit: The "real" script is more complex:
#!/usr/bin/env bash
echo "start script"
sandbox reset release -v
To make the sandbox
command available, see:
https://github.com/algorand/sandbox#getting-started
For now, I managed to get it working by setting a timeout safely over the time that the script takes to complete, and call done()
when the expected last log is shown:
test("script", (done) => {
// const script = cp.exec("./scripts/foo.sh");
const script = cp.exec("./scripts/sandbox_dev_reset.sh");
script.stdout.on("data", (data) => {
console.log("stdout: " data);
// this is a string that appears in the last log message
// don't know how to make the test stop (successfully) otherwise
if (
data.includes(
`Soon after sending the transaction it will appear in indexer`
)
) {
console.log("found the string! - done");
done();
}
});
script.stderr.on("data", (data) => {
console.error("stderr: " data);
});
}, 100000);
P.S. If it's of any help, originally this comes from a Rust test, where the script finishes normally (program continues executing without hacks):
pub fn reset_network(net: &Network) -> Result<()> {
let mut cmd = Command::new("sh");
cmd
.current_dir(format!("/foo/bar/scripts"))
.arg("./sandbox_dev_reset.sh"),
let reset_res = cmd
.stdout(Stdio::piped())
.spawn()?
.stdout
.expect("Couldn't reset network");
log::debug!("Script cmd stdout: {:?}", reset_res);
for line in BufReader::new(reset_res)
.lines()
.filter_map(|line| line.ok())
{
log::debug!("{}", line);
}
log::debug!("Script finished");
Ok(())
}
CodePudding user response:
See Testing Asynchronous Code:
It's common in JavaScript for code to run asynchronously. When you have code that runs asynchronously, Jest needs to know when the code it is testing has completed, before it can move on to another test. Jest has several ways to handle this.
You can use callbacks. When the script within that spawned shell is executed, call done()
to notify jest the test case is done. It is used for testing the callbacks, otherwise, the test case will not wait for the callback complete. This means the test case is executed, but your callback is not executed. It's different with the timeout
milliseconds option of the test()
function.
const cp = require('child_process');
const path = require('path');
test('script', (done) => {
const script = cp.exec(path.resolve(__dirname, './foo.sh'));
script.stdout.on('data', (data) => {
console.log('stdout: ' data);
done();
});
script.stderr.on('data', (data) => {
console.error('stderr: ' data);
done();
});
});
Test result:
PASS stackoverflow/74025882/index.test.js
✓ script (31 ms)
console.log
stdout: fooooo
at Socket.<anonymous> (stackoverflow/74025882/index.test.js:7:13)
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 1.175 s, estimated 10 s
update:
The default timeout of a test is 5000
milliseconds. If your script execution time is greater than this value, Jest will throw out a timeout error. This is its way of working. The timeout mechanism will abort those test cases that have been performed for too long.
You use the timeout
option for test()
function is right approach. See example:
const cp = require('child_process');
const path = require('path');
describe('74025882', () => {
test('script', (done) => {
const script = cp.exec(path.resolve(__dirname, './foo.sh'));
script.stdout.on('data', (data) => {
console.log('stdout: ' data);
done();
});
script.stderr.on('data', (data) => {
console.error('stderr: ' data);
done();
});
}, 10 * 1_000);
});
foo.sh
:
#!/usr/bin/env bash
sleep 6
echo "fooooo"
I use sleep 6
to simulate the execution time of your Rust script.