Cannot upload files when running tests in docker Selenium_Hub, Selenium-Node-Chrome-Debug

  • I am trying to run javascript mocha selenium-webdriver tests on my company's application using docker containers. I seem to have everything working except that I am unable to upload files. Prior to trying use my tests in a docker environment I was able to use it on our local servers and upload files with no issue using the a method like this:

    const companyImage = process.cwd()+ '/img/backgroundmario.jpg';
    const companyImageElem = await driver.findElement(By.xpath("//div/input[@type='file']"));
    await companyImageElem.sendKeys(companyImage);

    However, I have not been able to have any success when using docker containers. I mounted my img folder to my selenium/node-chrome-debug container which includes a VNC viewer and I can see that the images are present (and I can manually upload the images via the VNC viewer). However, despite numerous variations of providing paths to the images I cannot seem to get my images to upload. For some reason the working directory seems to be from my test container and not my node-chrome-debug container but even if I add the images to the test container and change the path to my test container directory with the images they do not upload either.

    Here is a snippet of my code I am using for my test (it includes some stuff I wouldn't normally include, specifically the check for process.cwd() and process.env.PWD since I just wanted to see what the path was):

    const {
    } = require('selenium-webdriver');
    const mocha = require('mocha');
    const chai = require("chai");
    const chaiAsPromised = require("chai-as-promised");
    const {
    } = require('./util')
    const fs = require('fs');
    const expect = require('chai').expect;
    const ciPassword = require('./envData').ciPassword;
    const campManagerMail  = '';
    const campManagerName = 'companycreator';
    const campManagerUsername = 'companycreator';
    const legacy = "http://node-web-client";
    const companyImage = '/opt/test/images/backgroundmario.jpg';
    var currentDir = process.cwd();
    var appFolder = process.env.PWD;
    const {
    } = require('./legacyCreationQueries');
    const {
    } = require('./sqlutil');
    describe('Creates a Company of Each Type via the Legacy Dashboard', function () {
       let driver;
       let util;
       before(async function () {
           driver = new Builder().forBrowser('chrome').usingServer('http://selenium_hub:4444/wd/hub').build();
           util = makeUtilityBelt(driver);
           await createLegacyCampManager(campManagerName, campManagerUsername, campManagerMail);
       afterEach(function () {
           let testCaseName = this.currentTest.title;
           let testCaseStatus = this.currentTest.state;
           if (testCaseStatus === 'failed') {
               driver.takeScreenshot().then((data) => {
                   let screenshotPath = `./results/${testCaseName}.png`;
                   console.log(`Saving Screenshot as: ${screenshotPath}`);
                   fs.writeFileSync(screenshotPath, data, 'base64');
       after(function () {
       describe('Load Legacy Corporate Site and Login to Legacy Dashboard', function () {
           it('Loads into the Legacy Dashboard Successfully', async function () {
               await driver.get(legacy);
               await driver.wait(until.elementLocated(By.xpath("//p[contains(text(),'Sign In')]"), 10000));
               await driver.sleep(3000);
               const emailElem = await driver.findElement(By.xpath("//input[@id='email']"));
               await util.sendKeys(emailElem, campManagerMail);
               const pwElem = await driver.findElement(By.xpath("//input[@id='password']"));
               await util.sendKeys(pwElem, ciPassword);
               await driver.findElement(By.xpath("//button[contains(text(),'Sign In')]")).click();
               await driver.wait(until.elementLocated(By.xpath("//div/ul[contains(@class, 'campaign-search-list')]"), 10000));
               await driver.wait(until.elementLocated(By.xpath("//ul[@class='menu']/li/a/span[contains(text(),'User Management')]"), 10000));
               await driver.sleep(5000);
               await console.log("Below is the Current Working Directory");
               await console.log(currentDir);
               await driver.sleep(3000);
               await console.log(appFolder);
               await driver.sleep(3000);
               await console.log("The above is the app folder");
               await driver.sleep(2000);
               const loginSuccessElem = await driver.findElement(By.xpath("//ul[@class='menu']/li/a/span[contains(text(),'User Management')]"));
               let loginSuccess = await loginSuccessElem.isDisplayed();
               await driver.sleep(3000);
               await expect(loginSuccess, 'Legacy Login Failed');
       describe('Creates a Reseller Company', function(){
           const companyName = 'Reseller Test Company';
           it('Navigates to Company Management and Begins Company Creation Process', async function(){
               await driver.wait(until.elementLocated(By.xpath("//ul[@class='menu']/li/a/span[contains(text(),'Company Management')]"), 10000));
               await driver.findElement(By.xpath("//ul[@class='menu']/li/a/span[contains(text(),'Company Management')]")).click();
               await driver.sleep(8000);
               await driver.wait(until.elementLocated(By.xpath("//h3[contains(text(),'Search Companies')]"), 10000));
               await driver.wait(until.elementLocated(By.xpath("//a[contains(text(),'+ Create Company')]"), 10000));
               await driver.sleep(8000);
               await driver.findElement(By.xpath("//a[contains(text(),'+ Create Company')]")).click();
               await driver.wait(until.elementLocated(By.xpath("//h3[contains(text(),'Create Company')]"), 10000));
               const companyCreationPageElem = await driver.findElement(By.xpath("//h3[contains(text(),'Create Company')]"));
               let companyCreationPage = await companyCreationPageElem.isDisplayed();
               await expect(companyCreationPage, 'Company Creation Page failed to Load');            
           it('Fills in the required fields and creates New Reseller Company', async function(){            
               const companyDescription = 'This is a test description for a random test company blah blah blah';
               const companyAddress = '777 Lucky Lane';
               const companyCity = 'Las Vegas';
               const companyState = 'Nevada';
               const companyZip = '89104';
               const companyNameElem = await driver.findElement(By.xpath("//input[@label='Company Name']"));
               await util.sendKeys(companyNameElem, companyName);
               await driver.sleep(1000);
               const companyDescriptionElem = await driver.findElement(By.xpath("//textarea[@label='Company Description']"));
               await util.sendKeys(companyDescriptionElem, companyDescription);
               await driver.sleep(1000);
               const companyTypeElem = await driver.findElement(By.xpath("//select"));
               await driver.wait(until.elementLocated(By.xpath("//select/option"), 10000));
               const companyTypeSelectElem = await driver.findElement(By.xpath("//select/option[@value='1']"));
               await driver.sleep(1000);
               const addressElem = await driver.findElement(By.xpath("//input[@label='Address']"));
               await util.sendKeys(addressElem, companyAddress);
               await driver.sleep(1000);
               const cityElem = await driver.findElement(By.xpath("//input[@label='City']"));
               await util.sendKeys(cityElem, companyCity);
               await driver.sleep(1000);
               const stateElem = await driver.findElement(By.xpath("//input[@label='State']"));
               await util.sendKeys(stateElem, companyState);
               await driver.sleep(1000);
               const zipElem = await driver.findElement(By.xpath("//input[@label='Zip Code']"));
               await util.sendKeys(zipElem, companyZip);
               await driver.sleep(1000);
               await driver.findElement(By.xpath("//input[@type='file']")).sendKeys(companyImage);
               await driver.sleep(1000);
               await driver.wait(until.elementLocated(By.xpath("//img[@class='image-preview']"), 10000));
               await driver.sleep(1000);
               const submitButtonElem = await driver.findElement(By.xpath("//button[contains(text(),'Submit')]"));
               await driver.wait(until.elementLocated(By.xpath("//h3[contains(text(),'Company Actions')]"), 10000));
               await driver.wait(until.elementLocated(By.xpath("//p[@class='company-name']"), 10000));
               const companySuccessElem = await driver.findElement(By.xpath("//p[@class='company-name'][contains(text(),'"+companyName+"')]"));
               let companySuccess = await companySuccessElem.isDisplayed();
               await expect(companySuccess, 'Failed to Create New Company');

    This is the last thing stopping me from integrating my large number of test files with our CI/CD process but a huge number of my tests involve uploading files so it is a major issue.

    Note: This is an edited copy of a question I asked on Stack Overflow. I am getting desperate because of my lack of progress.

    Update: The error I am receiving is: InvalidArgumentError: invalid argument: File not found : /opt/test/images/backgroundmario.jpg (Session info: chrome=77.0.3865.75). This is puzzling to me in several ways based on what I have read and what people in the official Selenium Slack channel had told me.

    First of all, in my docker_test_1 container (which contains the test file) the path opt/test/images/ ``` actually does point to the file. However, I was told in the Selenium Slack that I needed to have my images in the node-chrome-debug container with the path opt/images

    So I mounted the images folder to the selenium-chrome-debug container in opt/images but if I use that path in my test file it does not attach the image either and I receive a similar error. However, if I use VNC viewer I can see that in opt/images the images are there and I can manually upload them through the VNC browser. So despite trying numerous paths I can not seem to get my images to upload despite the fact that when running my tests on a local environment without docker the images upload without issue.

    Another thing that puzzled me is that the result of

    ``` var currentDir = process.cwd(); var appFolder = process.env.PWD; ```

    is opt/test when I would have assumed that it would have been the working directory for the selenium-node-chrome container based on what I was told in the Selenium Slack channel.

    I tried changing the path for my images to opt/test/images/ but they still do not upload.

    I am not someone who tries ask questions for something that is a simple google search away but I have been struggling for several days with this issue and I can't seem to find any solution or helpful advice.

  • Despite not receiving any guidance here other than someone wanting to edit my text for clarity despite providing any actual value as far as the actual question goes, I was able to find a solution to the issue based on some additional research, some help from a coworker and some experimentation.

    So there are several aspects that are important. First of all, you must make sure that the images you want to upload are mounted to the container with your browser (in my case, selenium/node-chrome-debug). Then you must make some additions to your test Selenium test file.

    You must add the following lines:

    var path = require('path');
    var remote = require('selenium-webdriver/remote');

    You can use var or let although I've been told that let is a better standard practice.

    Then, after the line

    driver = new Builder().forBrowser('chrome').usingServer('http://selenium_hub:4444/wd/hub').build();

    Add this line of code

    driver.setFileDetector(new remote.FileDetector());

    For your the file you wish to upload, you must set the path to that of the file system on the browser container (selenium/node-chrome-debug in my case). So your file variable would be something like:

    const companyImage = process.cwd()+'/images/backgroundmario.jpg';

    Then, when you want to upload the file, you find the respective element using whichever form of identification you like and add a little extra to the sendKeys function in comparison to how you would do it were you simply running the script on your local file system rather than a docker container. So the code would look like this:

    await driver.findElement(By.xpath("//input[@type='file']")).sendKeys(path.resolve(__dirname, companyImage));

    Maybe there is a slightly cleaner way to code it (for example, I generally declare the elements I am interacting with as variables) but the example I have provided will work. It took me a lot of time and effort to find this solution so I hope this eventually saves someone else the amount of pain I experienced trying to get this to work.

    While I hope this answer saves someone some time facing this issue I would say that the QA section of Stack Exchange seems to be largely useless as far as the input provided and I am completely turned off by my experience. Thanks a lot for that Kate Paulk. I sincerely hope you are feeling the effects of the wage gap since rather than trying to address an issue you decided to attack minute aspects of how the issue was phrased.

Suggested Topics

  • 2
  • 2
  • 2
  • 2
  • 2
  • 2
  • 2
  • 2