Checking if File Was Downloaded on Windows With Protractor - javascript

I am trying to run a test where a file gets downloaded from our web application. The problem that I am running into however is that the file already exists and I have an if statement to handle it and delete it prior to downloading another one. The problem is though that it never goes into the if statement.
I know that my file path is correct. Is there something else I am doing wrong here?
const fs = require('fs')
fit('Download Visible Records', () => {
const filename = `C:\\Users\\me\\Downloads\\DataExport_2017-04-18-10-04am.csv`
if (fs.existsSync(filename)) {
console.log('Are we getting here??') // NEVER ACTUALLY GETS HERE EVEN THOUGH THE FILE DOES EXIST
// Make sure the browser doesn't have to rename the download
fs.unlink(filename)
console.log('how about here?')
}
claims.downloadVizRecords()
browser.driver.wait(() => {
expect(fs.existsSync(filename)).toBe(true)
}, 10000)
})

My code works fine:
if (fs.existsSync('D:/CI/script/result/Report.html')) {
fs.unlinkSync('D:/CI/script/result/Report.html');
}

Related

How to clear browser cache within a Cypress test

Background:
Hey all, I have a Cypress test I'm trying to write that should check whether an image URL has been called, after the image itself has been clicked on.
Currently I run the test and it finds the image URL on the first run but when it is re-run, the test fails to find it because the image has already been saved to the browser cache.
Is there a way to clear the browser cache before each Cypress test so that the image URL will always get called/found when I run the test?
Current solution:
it('should request webp image format for zoomed pdp image', () => {
cy.intercept(
'**/cdn/cs/set/assets/blt2bd0405f3cb027cb/5005769.jpg?fit=bounds&format=webply&quality=80&width=1600&height=1600&dpr=1.5'
).as('zoomedImageRequest');
cy.gotoPage('product/transparent-red-sorting-case-to-go-5005769');
cy.get(pdp.pdpImage).click();
cy.wait('#zoomedImageRequest').then((xhr) => {
const zoomedImageUrl = xhr.response.url;
expect(zoomedImageUrl).to.contain('format=webply');
});
});
Try clearing the network cache at the top of the test,
cy.wrap(Cypress.automation('remote:debugger:protocol', {
command: 'Network.clearBrowserCache',
}))
cy.intercept(...).as('zoomedImageRequest');
cy.gotoPage('product/transparent-red-sorting-case-to-go-5005769');
cy.get(pdp.pdpImage).click();
cy.wait('#zoomedImageRequest').then((xhr) => {
const zoomedImageUrl = xhr.response.url;
expect(zoomedImageUrl).to.contain('format=webply');
});
There is also a header that can be removed, but I found it not to be consistent
cy.intercept('...', req => {
delete req.headers['if-none-match'];
})

saving file with content as number results with NaN content

I have a simple script in my nodejs app which is managing file. When application is started it works indefinitely and on fixed intervals its saving current value (that is a number).
Lets say we have counter.dat file and its content is number 6570.
App is launched and its working, file is being updated properly for days.
In some occasions i have to restart the process and sometimes (rarely) i find NaN inside instead of proper number.
I made some tests and it seems that starting file and exiting it can indeed sometimes store NaN.
const init = async () => {
...
const readCounter = await files.getContent("counter.dat");
...
};
const saveCounter = (counter) => {
// this value (counter) was never found NaN, but sometimes file has NaN inside
let stream = fs.createWriteStream("counter.dat");
stream.once('open', () => {
stream.write(counter);
stream.end();
});
};
Can it be that exiting script (for example with CTRL + C in MAC terminal) can cause number to be saved as NaN and if so, what could be done to prevent it?
Yes, It's clear if you enter CTRL + c then your process will close.
You must keep your script alive even if you close the terminal.
There is a module called pm2, you can start your script by pm2.

for loop continues to run even when "break" is used

I am trying to run a check within a folder that will display only one alert if there is any other file than extension - '.txt'
Here is my code:
for (let file of files) {
const ext = path.extname(file);
console.log(typeof file)
if (ext != '.txt') {
console.log(ext)
console.log("The error should have popped up")
dialog.showErrorBox('Non TXT file detected', 'there is a non-txt file in the folder')
break
}
else {
console.log("Not Working")
}
}
The issue that I am having is that say there were 10 ".txt" files in the folder, if I renamed one of them to ".test" I would receive 10 alerts. Is there a way to as soon as the "dialog.showErrorBox" has been called prevent to for loop for continuing.
I receive the same amount of errors no matter which file i change the extension for.
I replicated your code, and it seems to work fine: https://repl.it/repls/ExcellentVapidApplicationframework
Maybe an issue caused by ASI, as Andreas suggested?

fs.watch calling method when not supposed to?

I'm rather new to node.js and javascript aswell, and I keep running into an error with the code below.
fs.watch('F:/junk', (eventType, filename) => {
if(filename && filename.split('.')[1].match("zip")) {
try {
var zip = new azip(dir + filename);
} catch(err) { return console.log(err); }
try {
zip.extractAllTo(dir + filename.split('.')[0], false);
} catch(err) { return console.log(err); }
}
});
and the error. the error only occurs after successfully running extractAllTo (adm-zip), and doesn't occur at all if I take that bit out.
if(filename && filename.split('.')[1].match("zip")) {
TypeError: Cannot read property 'match' of undefined
I'm using the same file for testing every time, with the name dummyfile.zip
I can create another if statement within the first one and it'll work fine, but seems a bit redundant doing it that way
You must test for the existence of array element 2 (or index 1). There are numerous reasons. Without knowing operating system and file structure it is impossible to tell you. For instance, fs.watch() will return a directly - if the directory has no '.', then fail. This is the same if a file has no extension.
To validate this, in the console do this:
let str = "directory name";
str.split('.');
str.split('.')[1].match('test');
view the output.
Consider
if(filename && filename.includes('.zip')) {
// work
}
}
code is a little more verbose, but will avoid the error caused by directories and files that do not have extensions and files with leading dots, etc.

Re-using same instance again webdriverJS

I am really new to Selenium. I managed to open a website using the below nodejs code
var webdriver = require('selenium-webdriver');
var driver = new webdriver.Builder()
.forBrowser('chrome')
.build();
console.log(driver);
driver.get('https://web.whatsapp.com');
//perform all other operations here.
https://web.whatsapp.com is opened and I manually scan a QR code and log in. Now I have different javascript files to perform actions like delete, clear chat inside web.whatsapp.com etc...
Now If I get some error, I debug and when I run the script again using node test.js, it takes another 2 minutes to load page and do the steps I needed. I just wanted to reopen the already opened tab and continue my script instead new window opens.
Edit day 2 : Still searching for solution. I tried below code to save object and reuse it.. Is this the correct approach ? I get a JSON parse error though.
var o = new chrome.Options();
o.addArguments("user-data-dir=/Users/vishnu/Library/Application Support/Google/Chrome/Profile 2");
o.addArguments("disable-infobars");
o.addArguments("--no-first-run");
var driver = new webdriver.Builder().withCapabilities(webdriver.Capabilities.chrome()).setChromeOptions(o).build();
var savefile = fs.writeFile('data.json', JSON.stringify(util.inspect(driver)) , 'utf-8');
var parsedJSON = require('./data.json');
console.log(parsedJSON);
It took me some time and a couple of different approaches, but I managed to work up something I think solves your problem and allows to develop tests in a rather nice way.
Because it does not directly answer the question of how to re-use a browser session in Selenium (using their JavaScript API), I will first present my proposed solution and then briefly discuss the other approaches I tried. It may give someone else an idea and help them to solve this problem in a nicer/better way. Who knows. At least my attempts will be documented.
Proposed solution (tested and works)
Because I did not manage to actually reuse a browser session (see below), I figured I could try something else. The approach will be the following.
Idea
Have a main loop in one file (say init.js) and tests in a separate file (test.js).
The main loop opens a browser instance and keeps it open. It also exposes some sort of CLI that allows one to run tests (from test.js), inspect errors as they occur and to close the browser instance and stop the main loop.
The test in test.js exports a test function that is being executed by the main loop. It is passed a driver instance to work with. Any errors that occur here are being caught by the main loop.
Because the browser instance is opened only once, we have to do the manual process of authenticating with WhatsApp (scanning a QR code) only once. After that, running a test will reload web.whatsapp.com, but it will have remembered that we authenticated and thus immediately be able to run whatever tests we define in test.js.
In order to keep the main loop alive, it is vital that we catch each and every error that might occur in our tests. I unfortunately had to resort to uncaughtException for that.
Implementation
This is the implementation of the above idea I came up with. It is possible to make this much fancier if you would want to do so. I went for simplicity here (hope I managed).
init.js
This is the main loop from the above idea.
var webdriver = require('selenium-webdriver'),
by = webdriver.By,
until = webdriver.until,
driver = null,
prompt = '> ',
testPath = 'test.js',
lastError = null;
function initDriver() {
return new Promise((resolve, reject) => {
// already opened a browser? done
if (driver !== null) {
resolve();
return;
}
// open a new browser, let user scan QR code
driver = new webdriver.Builder().forBrowser('chrome').build();
driver.get('https://web.whatsapp.com');
process.stdout.write("Please scan the QR code within 30 seconds...\n");
driver.wait(until.elementLocated(by.className('chat')), 30000)
.then(() => resolve())
.catch((timeout) => {
process.stdout.write("\b\bTimed out waiting for code to" +
" be scanned.\n");
driver.quit();
reject();
});
});
}
function recordError(err) {
process.stderr.write(err.name + ': ' + err.message + "\n");
lastError = err;
// let user know that test failed
process.stdout.write("Test failed!\n");
// indicate we are ready to read the next command
process.stdout.write(prompt);
}
process.stdout.write(prompt);
process.stdin.setEncoding('utf8');
process.stdin.on('readable', () => {
var chunk = process.stdin.read();
if (chunk === null) {
// happens on initialization, ignore
return;
}
// do various different things for different commands
var line = chunk.trim(),
cmds = line.split(/\s+/);
switch (cmds[0]) {
case 'error':
// print last error, when applicable
if (lastError !== null) {
console.log(lastError);
}
// indicate we are ready to read the next command
process.stdout.write(prompt);
break;
case 'run':
// open a browser if we didn't yet, execute tests
initDriver().then(() => {
// carefully load test code, report SyntaxError when applicable
var file = (cmds.length === 1 ? testPath : cmds[1] + '.js');
try {
var test = require('./' + file);
} catch (err) {
recordError(err);
return;
} finally {
// force node to read the test code again when we
// require it in the future
delete require.cache[__dirname + '/' + file];
}
// carefully execute tests, report errors when applicable
test.execute(driver, by, until)
.then(() => {
// indicate we are ready to read the next command
process.stdout.write(prompt);
})
.catch(recordError);
}).catch(() => process.stdin.destroy());
break;
case 'quit':
// close browser if it was opened and stop this process
if (driver !== null) {
driver.quit();
}
process.stdin.destroy();
return;
}
});
// some errors somehow still escape all catches we have...
process.on('uncaughtException', recordError);
test.js
This is the test from the above idea. I wrote some things just to test the main loop and some WebDriver functionality. Pretty much anything is possible here. I have used promises to make test execution work nicely with the main loop.
var driver, by, until,
timeout = 5000;
function waitAndClickElement(selector, index = 0) {
driver.wait(until.elementLocated(by.css(selector)), timeout)
.then(() => {
driver.findElements(by.css(selector)).then((els) => {
var element = els[index];
driver.wait(until.elementIsVisible(element), timeout);
element.click();
});
});
}
exports.execute = function(d, b, u) {
// make globally accessible for ease of use
driver = d;
by = b;
until = u;
// actual test as a promise
return new Promise((resolve, reject) => {
// open site
driver.get('https://web.whatsapp.com');
// make sure it loads fine
driver.wait(until.elementLocated(by.className('chat')), timeout);
driver.wait(until.elementIsVisible(
driver.findElement(by.className('chat'))), timeout);
// open menu
waitAndClickElement('.icon.icon-menu');
// click profile link
waitAndClickElement('.menu-shortcut', 1);
// give profile time to animate
// this prevents an error from occurring when we try to click the close
// button while it is still being animated (workaround/hack!)
driver.sleep(500);
// close profile
waitAndClickElement('.btn-close-drawer');
driver.sleep(500); // same for hiding profile
// click some chat
waitAndClickElement('.chat', 3);
// let main script know we are done successfully
// we do so after all other webdriver promise have resolved by creating
// another webdriver promise and hooking into its resolve
driver.wait(until.elementLocated(by.className('chat')), timeout)
.then(() => resolve());
});
};
Example output
Here is some example output. The first invocation of run test will open up an instance of Chrome. Other invocations will use that same instance. When an error occurs, it can be inspected as shown. Executing quit will close the browser instance and quit the main loop.
$ node init.js
> run test
> run test
WebDriverError: unknown error: Element <div class="chat">...</div> is not clickable at point (163, 432). Other element would receive the click: <div dir="auto" contenteditable="false" class="input input-text">...</div>
(Session info: chrome=57.0.2987.133)
(Driver info: chromedriver=2.29.461571 (8a88bbe0775e2a23afda0ceaf2ef7ee74e822cc5),platform=Linux 4.9.0-2-amd64 x86_64)
Test failed!
> error
<prints complete stacktrace>
> run test
> quit
You can run tests in other files by simply calling them. Say you have a file test-foo.js, then execute run test-foo in the above prompt to run it. All tests will share the same Chrome instance.
Failed attempt #1: saving and restoring storage
When inspecting the page using my development tools, I noticed that it appears to use the localStorage. It is possible to export this as JSON and write it to a file. On a next invocation, this file can be read, parsed and written to the new browser instance storage before reloading the page.
Unfortunately, WhatsApp still required me to scan the QR code. I have tried to figure out what I missed (cookies, sessionStorage, ...), but did not manage. It is possible that WhatsApp registers the browser as being disconnected after some time has passed. Or that it uses other browser properties (session ID?) to recognize the browser. This is pure speculating from my side though.
Failed attempt #2: switching session/window
Every browser instance started via WebDriver has a session ID. This ID can be retrieved, so I figured it may be possible to start a session and then connect to it from the test cases, which would then be run from a separate file (you can see this is the predecessor of the final solution). Unfortunately, I have not been able to figure out a way to set the session ID. This may actually be a security concern, I am not sure. People more expert in the usage of WebDriver might be able to clarify here.
I did find out that it is possible to retrieve a list of window handles and switch between them. Unfortunately, windows are only shared within a single session and not across sessions.

Categories

Resources