0

Context

I am writing a "testing" utility for a specific use case.

We need the tests to be in separate files, and to be really simple to write, inspired by cypress and mocha.

For now I load the tests using require and it works well, but for the tests to be even more simple, I'd like to remove the first and last lines which are redundant.

What I have

// test.file.js :
const test = new (require("../index.js").Test)()
// to be removed

test.this(true)   // test is initialized
  .should("equal", true)  // (syntax inspired by cypress)

test.log(__dirname) 
// all node functionnalities (require...)
// are available

module.exports = test // to be removed
// importing logic :
function run (filePath) {
  const test = require(filePath)
  test.run()
} // much simplified

Here everything works fine but you need to require the library and do some logic and export the test in each test file.

What I would like

// test.file.js
test.log("hello world")

test.this(true)   // test is initialized
  .should("equal", true)

test.log(__dirname)  // node functions are available

Here test is already declared, initialized and available at scope level.

Exports are also automatic.

The problem

I'd like all node core functionalities to be available in the test BUT not the scope variables where I import the file.

What I tried

I achieved to make it work using some complicated eval, but the problem is that every local variables of the parent scopes are available for modification in the tests...

let hello = "world"
// this variable should not be available in the test

function run (testPath) {
  const test = new Test()
  // initialize the test

  const script = readFileSync(
    resolve(__dirname, testPath),
    {encoding: "utf-8"}
  )
  const scope = {
    test
  } // local variables i want the function to have access to

  eval(
    "(function ("
    + Object.keys(scope).join(", ")
    + ") {"
    + script
    +  "})"
  )(...Object.values(scope))

  console.log(hello)
  // here hello can be modified inside the test
  // this I don't want

  test.run() // works
}

I also tried with new Function(script) and some derivative but I could not find a solution

Question

How could I evaluate the test file in a clean node scope, scope with ALL the node functionalities AND only one shared variable which is test ?

Minimum reproducible example:

// test.file.js

test.log("hello from test")
test.variableProperty = "MODIFIED"
test.log(__dirname) // global node available
// test.log(hello) // should crash or hello be undefined
// index.js

class Test {
  constructor () {
    this.variableProperty = "INITIAL"
  }
  log (str) {
    console.log(str)
  }
  run () {
    console.log(this.variableProperty)
  }
}

// file import logic :
function importTest (path) {
  const test = new Test()
  // ...
  return test
}

// testing :
let hello = "should not appear"
const t = importTest("./test.file.js")
t.run()
// should output:
// "hello from test"
// (the __dirname)
// "MODIFIED"
// (and crash if last line of test.file.js uncommented)
marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
gui3
  • 1,711
  • 14
  • 30
  • 1
    a) just make these values global and use `require` as normally b) look at [the implementation of `require`](https://stackoverflow.com/a/28955050/1048572) and replicate it (or the parts that you need), adding the values you want to have in scope. For a simple version, see also [the `scope` parameter in this example](https://stackoverflow.com/a/24032179/1048572) – Bergi Oct 03 '21 at 17:18
  • tank you for your 3 solutions and for pointing me to this excellent and detailed answer, I'll give a try to each solution – gui3 Oct 03 '21 at 18:55

0 Answers0