2

I'm writing an automated test script using TestCafe and Node.JS. One of the tests is to download a file and validate that the download is complete. I don't want to write a hard coded

await t.wait(4000);

because since my test is based on a data driven framework I can feed it lots of data with lots of different files and file types. So the file size could vary by extreme amounts from a few Kilobytes to Gigabytes.

So writing

await t.wait(4000);

might work for one test case but will almost certainly fail for other test cases.

I'm using dependency 'downloads-folder' to discover the path to the downloads folder on the local system. From there I look for the specific file that I expect to be there. It works for small text files and small zip files that download fast. But as soon as I try to download a really large zip file with lots of data it fails

if (fs.existsSync(zipFileNameAndPath)) {
    await t.expect(true).eql(true); // Report it as a success.
} else {
    console.log('file does NOT exist: ' + zipFileNameAndPath);
  await t.expect(false).eql(true); // Report it as a failure!
}

So my question is:

Is there a way to do something like

if (fs.existsSync(zipFileNameAndPath){[ timeout: 50000000]}) {...} else {...}

Where the resulting timeout would work as a dynamic timeout, waiting to see when the file is found, and if it is found before the timeout expired then it would return true, and only after the timeout period has passed would it return false?

I'm looking for a Synchronous answer that doesn't have any dependencies on things like Lodash or JQuery and works fine in ES6.

Alex Skorkin
  • 4,264
  • 3
  • 25
  • 47
Seth Eden
  • 1,142
  • 2
  • 20
  • 42
  • You want to implement a _synchronous timeout_? Meaning, if the timeout is 30 seconds and the file is not found, your whole server will freeze and stop responding during 30 seconds? – Jeremy Thille Feb 21 '18 at 16:33
  • Yes that is correct. – Seth Eden Feb 21 '18 at 19:09
  • Why does it need to work synchronous when you're perfectly fine with promises and ES8 `await` syntax? Just use [a promise timeout](https://stackoverflow.com/a/37120577/1048572). – Bergi Feb 21 '18 at 21:30
  • @Seth you are aware that voluntarily freezing your server is the worse counter-productive idea of all time for your application, right? You're like, screw asynchronism, screw speed, screw uptime, screw anyone trying to connect to my server, and screw user experience. You know that, right? Tell me you know that – Jeremy Thille Feb 22 '18 at 05:26
  • The code is run through Node.JS on test machines it is NEVER pushed to the client code base or even close to any of the servers Dev, QA, Pre-Prod or Prod. We have multiple rigorous separation of concerns in that department. – Seth Eden Feb 22 '18 at 12:11
  • Besides TestCafe provides multiple provisions for timing out on Selectors anyway so it's no different than that, just that I'm doing it on the file system and not a UI selector on the page through the TestCafe test context object. – Seth Eden Feb 22 '18 at 12:43
  • @Bergi Thank you that is a good idea! Now that I got it working I need to move on to other things as I'm behind on implementation of test cases. But if I get the chance to revisit this, I'll look into that and see about implementing a promise based timeout. :-D – Seth Eden Feb 22 '18 at 12:47

3 Answers3

3
const filepath = 'YOUR_FILE.pdf';
const verifyDownload = async () => new Promise((resolve, reject) => {
    fs.watchFile(filepath, (curr, prev) => {
        if (fs.existsSync(filepath)) {
            fs.unwatchFile(filepath);
            resolve(true);
        }
     });
});

await t.expect(await verifyDownload()).ok("Could not download", { timeout: 30000 });
Arnolds
  • 31
  • 2
  • Oh this is a good alternative solution! I'll have to see about incorporating this. Very nice!! Thank you for sharing. :-D – Seth Eden Apr 26 '18 at 15:42
2

I wrote a function:

async function CheckFileExistsWithTimeDelay(t, timeDelay, fileNameAndPath) {
    for (var i = 0; i < timeDelay; i++) {
        console.log('Waited for a total of ' + i.toString() + ' microseconds');
        await t.wait(1);
        if (fs.existsSync(fileNameAndPath)) {
            // break;
            return;
        }
    }
};

Then the caller of that function looks like:

await Common.CheckFileExistsWithTimeDelay(t, 500000, zipFileNameAndPath);

Of course you could comment out the console.log(...), I just left it in there so I could watch it as it waited for debugging purposes.

When I ran it, it waited for 7996 microseconds while downloading a 59mb zip file before breaking out and continuing script execution successfully.

Could just as easily be Break; or Return; depending if you wanted to keep it as a function or use it directly in-line with your script code.

Seth Eden
  • 1,142
  • 2
  • 20
  • 42
0

The problem is you are checking for the file to exist without waiting for the download to complete. The code you posted is fine - you need to make sure it's not invoked until after the download completes (or fails).

How to do that is dependent on how you are doing the download. It could be as simple as dropping an 'await' on the line of code that does the download.

Robert Levy
  • 28,747
  • 6
  • 62
  • 94
  • Oh that is an interesting idea. I hadn't thought of that. I'm not sure if TestCafe will complain about t.click will work without the await. I'm running a long series of script suites right now after consolidating a bunch of my constants libraries. So I'll give this a try when those test runs are done, and report back then. Thank you! – Seth Eden Feb 21 '18 at 19:13
  • Well removing the await alone didn't solve this. Maybe I'll have to write a time-delayed loop that checks if the file is present and only fails when the time on the loop runs out. – Seth Eden Feb 21 '18 at 20:11