0

I'm working on implementing a test that reads a list of files from a given directory. The list is then checked against a Map object that uses filenames as keys. If a match is found I want to execute some tests, if not skip the file and move on to the next one.

I have the test working if I just grab one object from the Map but it fails when I try to loop. I want to run tests by looping over multiple files.

It only seems to be a problem if I have async calls in my tests. Using a completely synchronous loop I can get it to run just fine. Here is an example of the synchronous loop (I realize this is a very contrived example but it works as expected):

describe('a test', () => {
  const list = ['a', 'b', 'c'];

  const test = (x) => {
    describe(`should run ${x}`, () => {
        beforeAll(async () => {
            console.log('beforeAll');
        });

        beforeEach(async () => {
            console.log('beforeEach');
        });

        for(let i = 0; i < 2; i++) {
          it(`should log iteration ${i}`, async () => {
            console.log(x, i);

            expect(i).toBeTruthy();
          });
        }
    });
  }

  for (let i = 0; i < list.length; i++) {
    test(list[i]);
  }
});

The output from the above snippet suggests that it runs just fine: enter image description here

This same pattern does not work if it is asynchronous though. In fact, the nested describe in test isn't even running as far as I can tell. I logged the project variable to the console so I could see that it was getting called but the logging in... statement in the beforeAll isn't getting executed at all. It seems like it's just skipping right over it. How do I make this work with async/await?

describe('Import Project', () => {
  const _page = new PageFactory();
  const utils = new Utils();
  const projPath = browser.params.projPath;

  let projectGuid: string;

  const test = (project) => {
    describe(`importing ${project.name}`, () => {
        beforeAll(async () => {
            console.log('logging in...'); //not getting executed for some reason
            await _page.loginPage.login(browser.params.username, browser.params.password);

            // import
            await _page.projectsPage.importProject(projectPath + project.filename)
                .withImportType(project.importType)
                .withProjectName(project.projectName)
                .inEnvironment(project.environment)
                .import();

            projectGuid = await utils.extractGuid();
        });

        afterEach(async () => {
            await _page.designerPage.navigateTo(projectGuid);
        });

        for(let i = 0; i < project.operations.length; i++) {
            const operation = project.operations[i];

            it(`should run ${operation.name} successfully`, async () => {
                await runOperation(operation.name);

                const status = await _page.operationLogsPage.getStatus(operation.outcomeTarget);
                expect(status).toEqual(operation.expectedStatus);
            });
        }
    });
  }

  utils.getProjects().then(projects => {
    // projects is a string[] of filenames
    // that are returned using fs.readdir()
    for (let i = 0; i < projects.length; i++) {
        const myProject = projectMap.get(projects[i]);

        if (myProject) {
            test(myProject);
        }
    }
  });
});

The output from this is just Executed 0 of 0 specs SUCCESS in 0.003 sec.

I don't understand how this would be any different than manually writing out all these nested describe blocks. If I wrote each one out it would work just fine but for some reason in a loop it doesn't want to work. Any ideas?

tehbeardedone
  • 2,833
  • 1
  • 15
  • 23
  • For the record, I also tried awaiting `test`. That did not work either. – tehbeardedone Apr 10 '19 at 21:55
  • for loop does not wait assync routine inside it. this is related to this question https://stackoverflow.com/questions/11488014/asynchronous-process-inside-a-javascript-for-loop – SET001 Apr 10 '19 at 22:10
  • That answer suggests several things that don't work. `.forEach(async p => { // do stuff }` fails with the same result. Putting the loop inside an async function like `async function something() { for(..) { await test(project) } }` doesn't work either. – tehbeardedone Apr 10 '19 at 22:53

1 Answers1

0

I couldn't figure out how to get this to work the way I wanted. Instead I decided to use node's child_process.execSync and looped through each file that way. It's not ideal but it works. Takes a bit longer since each file I want to run tests on creates a new session but I'm tired of trying to make it work the other way.

I'm running the script using node ./path/to/test.js --baseUrl=http://<myUrl>

Here's the relevant part of the code:

fs.readdir(projectsPath, (err, files) => {
  if (err) {
    throw new Error('Unable to scan directory: ' + err);
  }

  let i = 0;

  const next = () => {
    if (i < files.length) {
        const file = files[i];
        console.log('running tests on:', file);            

        const runCmd = `nodenv --exec "yarn run e2e:all ${baseUrl} --params.projectToImport=${file} --suite import_projects"`            

        // exec
        try {
            execSync(runCmd, { cwd: workingDir, stdio: 'inherit' },
                (error, stdout, stderr) => {
                    if (error) {
                        console.error(error);
                        return;
                    }

                    console.log(stdout);
                });
        } catch (err) {
            if (err.stderr) {
                console.log('stderr:', err.stderr.toString());
            }
        }

        // next iteration
        i++;
        next();
      }
  }

  next();
});
tehbeardedone
  • 2,833
  • 1
  • 15
  • 23