Variable scope in protractor - scope

I am running protractor and jasmine to run unit tests.
I need to know the build version of my web app in order to execute different tests.
I have declared a variable to store this version value.
var version ='';
I am getting the version number by using the following code.
menuObject.modaltext.getText().then(function(text) {
version = text.slice(79,86);
console.log(version);
browser.driver.sleep(7000);
});
The version number is acquired correctly and is consoled properly.
But when i use this version outside of this .then function, its value becomes undefined and I am unable to check for any conditions using that variable.
How can i access the version number so that I use it to control the flow of the tests.
![version variable is highlighted, I am unable to access the version inside the if cases]

Try changing var to let. This allows to access your version inside your specs.
describe('Nodeprojectpart2Component', () => {
let version = '';
beforeEach(() => {
version = '1.0';
});
it('test', () => {
console.log( 'version' + version);
});
});
Issue with your code - Your are retrieving the value of version inside an asynchronous/callback function. Now before your function executes, your console is executed and prints undefined. I am not sure why would you like to define code outside specs. But if you still want to, you shall have the retrieval logic right after you define it in desribe block, something like -
describe('Nodeprojectpart2Component', () => {
let version = '';
version = //logic to find the version here itself
....

Related

How can I run a Go WASM program using Node.js?

I created a test WASM program using Go. In the program's main, it adds an API to the "global" and waits on a channel to avoid from exiting. It is similar to the typical hello-world Go WASM that you can find anywhere in the internet.
My test WASM program works well in Browsers, however, I hope to run it and call the API using Node.js. If it is possible, I will create some automation tests based on it.
I tried many ways but I just couldn't get it work with Node.js. The problem is that, in Node.js, the API cannot be found in the "global". How can I run a GO WASM program (with an exported API) in Node.js?
(Let me know if you need more details)
Thanks!
More details:
--- On Go's side (pseudo code) ---
func main() {
fmt.Println("My Web Assembly")
js.Global().Set("myEcho", myEcho())
<-make(chan bool)
}
func myEcho() js.Func {
return js.FuncOf(func(this js.Value, apiArgs []js.Value) any {
for arg := range(apiArgs) {
fmt.Println(arg.String())
}
}
}
// build: GOOS=js GOARCH=wasm go build -o myecho.wasm path/to/the/package
--- On browser's side ---
<html>
<head>
<meta charset="utf-8"/>
</head>
<body>
<p><pre style="font-family:courier;" id="my-canvas"/></p>
<script src="wasm_exec.js"></script>
<script>
const go = new Go();
WebAssembly.instantiateStreaming(fetch("myecho.wasm"), go.importObject).then((result) => {
go.run(result.instance);
}).then(_ => {
// it also works without "window."
document.getElementById("my-canvas").innerHTML = window.myEcho("hello", "ahoj", "ciao");
})
})
</script>
</body>
</html>
--- On Node.js' side ---
globalThis.require = require;
globalThis.fs = require("fs");
globalThis.TextEncoder = require("util").TextEncoder;
globalThis.TextDecoder = require("util").TextDecoder;
globalThis.performance = {
now() {
const [sec, nsec] = process.hrtime();
return sec * 1000 + nsec / 1000000;
},
};
const crypto = require("crypto");
globalThis.crypto = {
getRandomValues(b) {
crypto.randomFillSync(b);
},
};
require("./wasm_exec");
const go = new Go();
go.argv = process.argv.slice(2);
go.env = Object.assign({ TMPDIR: require("os").tmpdir() }, process.env);
go.exit = process.exit;
WebAssembly.instantiate(fs.readFileSync(process.argv[2]), go.importObject).then((result) => {
go.run(result.instance);
}).then(_ => {
console.log(go.exports.myEcho("hello", "ahoj", "ciao"));
}).catch((err) => {
console.error(err);
process.exit(1);
});
This pseudo code represents 99% content of my real code (only removed business related details). The problem is that I not only need to run the wasm program (myecho.wasm) by Node.js, but I also need to call the "api" (myEcho), and I need to pass it parameters and receive the returned values, because I want to create automation tests for those "api"s. With Node.js, I can launch the test js scripts and validate the outputs all in the command line environment. The browser isn't a handy tool for this case.
Running the program by node wasm_exec.js myecho.wasm isn't enough for my case.
It would be nice to know more details about your environment and what are you actually trying to do. You can post the code itself, compilation commands, and versions for all the tools involved.
Trying to answer the question without these details:
Go WASM is very browser oriented, because the go compiler needs the glue js in wasm_exec.js to run. Nodejs shouldn't have a problem with that, and the following command should work:
node wasm_exec.js main.wasm
where wasm_exec.js is the glue code shipped with your go distribution, usually found at $(go env GOROOT)/misc/wasm/wasm_exec.js, and main.wasm is your compiled code. If this fails, you can post the output as well.
There is another way to compile go code to wasm that bypasses wasm_exec.js, and that way is by using the TinyGo compiler to output wasi-enabled code. You can try following their instructions to compile your code.
For example:
tinygo build -target=wasi -o main.wasm main.go
You can build for example a javascript file wasi.js:
"use strict";
const fs = require("fs");
const { WASI } = require("wasi");
const wasi = new WASI();
const importObject = { wasi_snapshot_preview1: wasi.wasiImport };
(async () => {
const wasm = await WebAssembly.compile(
fs.readFileSync("./main.wasm")
);
const instance = await WebAssembly.instantiate(wasm, importObject);
wasi.start(instance);
})();
Recent versions of node have experimental wasi support:
node --experimental-wasi-unstable-preview1 wasi.js
These are usually the things you would try with Go and WASM, but without further details, it is hard to tell what exactly is not working.
After some struggling, I noticed that the reason is simpler than I expected.
I couldn't get the exported API function in Node.js simply because the API has not been exported yet when I tried to call them!
When the wasm program is loaded and started, it runs in parallel with the caller program (the js running in Node).
WebAssembly.instantiate(...).then(...go.run(result.instance)...).then(/*HERE!*/)
The code at "HERE" is executed too early and the main() of the wasm program hasn't finished exporting the APIs yet.
When I changed the Node script to following, it worked:
WebAssembly.instantiate(fs.readFileSync(process.argv[2]), go.importObject).then((result) => {
go.run(result.instance);
}).then(_ => {
let retry = setInterval(function () {
if (typeof(go.exports.myEcho) != "function") {
return;
}
console.log(go.exports.myEcho("hello", "ahoj", "ciao"));
clearInterval(retry);
}, 500);
}).catch((err) => {
console.error(err);
process.exit(1);
});
(only includes the changed part)
I know it doesn't seem to be a perfect solution, but at least it proved my guess about the root cause to be true.
But... why it didn't happen in browser? sigh...

How do I override config values at runtime with node-config?

I'd like to override some values at test-time, specifically setting my retries for an http service to 1 (immediate failure, no retries). Our project uses node-config. According to the docs I can override with NODE_CONFIG env variable:
node myapp.js --NODE_CONFIG='{"Customer":{"dbConfig":{"host":"customerdb.prod"}}}'
Well I would prefer to do this in my test, but not for all tests. The code says that you can allow config mutations by setting ALLOW_CONFIG_MUTATIONS.
process.env.ALLOW_CONFIG_MUTATIONS = "true";
const importFresh = require('import-fresh');
importFresh("config");
process.env.NODE_CONFIG = JSON.stringify({httpServices:{integration:{enrich: {retryInterval: 1, retries: 1}}}});
expect(process.env.NODE_CONFIG, 'NODE_CONFIG not set').to.exist();
expect(process.env.NODE_CONFIG, 'NODE_CONFIG not set').to.match(/retryInterval/);
expect(process.env.ALLOW_CONFIG_MUTATIONS, 'ALLOW_CONFIG_MUTATIONS not set').to.equal("true");
const testConfig = require("config");
console.dir(testConfig.get("httpServices.integration.enrich"));
expect(testConfig.get("httpServices.integration.enrich.retryInterval"), 'config value not set to 1').to.equal(1);
Result:
{ url: 'https://internal-**********',
retryInterval: 5000,
retries: 5 }
`Error: config value not set to 1: Expected 5000 to equal specified value: 1`
How do I get this override to work?
(expect is from Hapi.js Code library)
I'm one of the maintainers of node-config. Your bug is that you used require the second time when you should have used importFresh again.
Your first use of "importFresh()" does nothing different than require() would, because it is the first use of require().
After setting some variables, you call require(), which will return the copy of config already generated and cached, ignoring the effects of the environment variables set.
You only needed to use importFresh() once, where you currently use require(). This will cause a "fresh" copy of the config object to be returned, as you expected.
Simply changing config's property worked for me.
For example:
const config = require( 'config' );
config.httpServices.integration.enrich.retryInterval = 1;
// Do your tests...
UPD: Make sure that overrides are done before anyone calls the first config.get(), because the config object is made immutable as soon as any client uses the values via get().
Joining late, but other answers did not fit with the testing standard in my project, so here is what I came up with
TL;DR
Use mocks..
Detailed Answer
node-config uses a function get to get the configuration values.
By mocking the function get you can easily modify any configuration you see fit..
My personal favorite library is sinon
Here is an implementation of a mock with sinon
const config = require('config');
const sinon = require('sinon');
class MockConfig {
constructor () {
this.params = {};
this.sandbox = sinon.sandbox.create();
}
withConfValue (confKey, confValue) {
this.params.confValues[confKey] = confValue;
return this;
}
reset () {
this.params.confValues: {};
return this;
}
restore() {
this.sandbox.restore();
}
apply () {
this.restore(); // avoid duplicate wrapping
this.sandbox.stub(config, 'get').callsFake((configKey) => {
if (this.params.confValues.hasOwnProperty(configKey)) {
return this.params.confValues[configKey];
}
// not ideal.. however `wrappedMethod` approach did not work for me
// https://stackoverflow.com/a/57017971/1068746
return configKey
.split('.')
.reduce((result, item) => result[item], config)
});
}
}
const instance = new MockConfig();
MockConfig.instance = () => instance;
module.exports = MockConfig;
Usage would be
const mockConfig = require('./mock_config').instance();
...
beforeEach(function () {
mockConfig.reset().apply();
})
afterEach(function () {
mockConfig.reset().clear();
})
it('should do something') {
mockConfig.withConfValue('some_topic.some_field.property', someValue);
... rest of the test ...
}
Assumptions
The only assumption this approach makes is that you adhere to node-config way of reading the configuration (using the get function) and not bypass it by accessing fields directly.
It's better to create a development.json, production.json et test.json in your config folder node-config will use it your app configuration.
you just net to set your NODE_ENV to use the specific file.
Hope it helps :)

Is DB a reserved word in Node.js

I am building some tests with mocha and chai(expect).
Keeping it simple as I am learning about testing methodology as I go along.
I have a mysql db layer in a config file.
Testing the db parameters, I ran into a weird issue.
These db parameters test fine:
host= 'localhost',
user='foo',
password='bar',
The tests:
var expect = require('chai').expect;
var db = require('../db/config.ini');
describe('Database Access', function() {
it('HOST parameter should be a string', function() {
expect(host).to.be.a('string');
});
it('USER parameter should be a string', function() {
expect(user).to.be.a('string');
});
it('PASSWORD parameter should be a string', function() {
expect(password).to.be.a('string');
});
it('DB parameter should be a string', function() {
expect(db).to.be.a('string');
});
it('HOST parameter should equal localhost', function() {
expect(host).to.equal('localhost');
});
it('USER parameter should equal foo', function() {
expect(user).to.equal('foo');
});
it('PASSWORD parameter should equal bar', function() {
expect(password).to.equal('bar');
});
it('DB parameter should equal thatone', function() {
expect(context).to.equal('thatone');
});
});
When I add the database to choose,
db='thatone';
The test fails the parameter because it reads it as an object.
1) Database Access DB parameter should be a string:
AssertionError: expected {} to be a string
at Context.<anonymous> (test/db_tests.js:21:20)
If I change the variable name to "context" the test passes as expected.
I'm wondering if there is something obvious I am missing about using "db" as a variable.
UPDATE
Really stupid, novice level mistake.
So focused on learning testing methodology I didn't realize I had created the
'db' var as a require to the 'ini' and then referenced it later as though it was unique.
Really dumb. Rushing through this recklessly to get to a destination, and failing to follow some good methodology.
The result of executing this is not a string:
var db = require('../db/config.ini');
It seems you are trying to get a file in some INI dialect to be meaningfully interpreted by Node. Node does not support this by default. If you do not get an error while loading it, the most likely reason is that the text you have in there happens to also be valid JavaScript but since INI files do not contain proper code to export something (i.e. the file does not contain exports.db = "something" or module.exports = { ... } or something similar), then the module has the value {}.
You need to add one of the multiple npm packages that will automatically interpret an INI file and provide a meaningful value. I cannot recommend one as I don't use INI files in my software but you can search npm for a package that will perform the translation for you.

Un-requiring node ShouldJS

My node app has a fairly large test suite that depends on ShouldJS module. The problem is that the library blocks assigning .should properties on objects, but my test suite needs to spin up a server that needs to be mockable (i.e. run in the same process as my test suite) and dynamically build ElasticSearch queries, that need to set .should property on Objects.
I tried to un-require ShouldJS using an approach described here. But that didn't help - see example script below. Is there a workaround or an alternative approach?
Example:
require('should');
var objBefore = {};
objBefore.should = 'should1'; // doesn't get assigned!
objBefore['should'] = 'should2'; // doesn't get assigned!
console.log('Before:', objBefore);
Object.keys(require.cache).forEach(function(path) {
if (path.indexOf('/node_modules/should/') === -1) return;
console.log('Un-requiring path', path);
delete require.cache[path];
});
var objAfter = {};
objAfter.should = 'should1'; // doesn't get assigned!
objAfter['should'] = 'should2'; // doesn't get assigned!
console.log('After:', objAfter);
// still has shouldJS stuff
console.log('objAfter.should:', objAfter.should);
which gives this:
Before: {}
Un-requiring path /my/path/node_modules/should/index.js
Un-requiring path /my/path/node_modules/should/lib/should.js
...
Un-requiring path /my/path/node_modules/should/lib/ext/contain.js
Un-requiring path /my/path/node_modules/should/lib/ext/promise.js
After: {}
objAfter.should: Assertion { obj: {}, anyOne: false, negate: false, params: { actual: {} } }
None of the .should properties are getting assigned.
There's a way to turn ShouldJS should getter on and off:
require('should');
(1).should.be.equal(1);
// turn off ShouldJS should getter
// and use should as a function
var should = require('should').noConflict();
should(0).be.equal(0);
// turn on ShouldJS should getter
should.extend();
require('should');
(1).should.be.equal(1);
Here's what the official Readme says:
It is also possible to use should.js without getter (it will not even try to extend Object.prototype), just require('should/as-function'). Or if you already use version that auto add getter, you can call .noConflict function.
Results of (something).should getter and should(something) in most situations are the same

Purescript pulp build output generates requirejs error in browser

When I use pulp build -O -t html/main.js and then pulp build -O -I test -m Test.Main -t html/testmain.js (i.e. building main and test) I get two different js output. In the former case, the format is
// Generated by psc-bundle 0.8.2.0
var PS = { };
(function(exports) {
// Generated by psc version 0.8.2.0
"use strict";
var Prelude = require("../Prelude");
var Control_Monad_Eff = require("../Control.Monad.Eff");
exports["main"] = main;
})(PS["Main"] = PS["Main"] || {});
PS["Main"].main();
Please note the require. In the latter case, the require is not in place at all
// Generated by psc-bundle 0.8.2.0
var PS = { };
(function(exports) {
/* global exports */
"use strict";
exports.concatArray = function (xs) {
return function (ys) {
return xs.concat(ys);
};
};
exports.showNumberImpl = function (n) {
/* jshint bitwise: false */
return n === (n | 0) ? n + ".0" : n.toString();
};
})(PS["Prelude"] = PS["Prelude"] || {});
(function(exports) {
// Generated by psc version 0.8.2.0
"use strict";
var $foreign = PS["Prelude"];
var Semigroupoid = function (compose) {
this.compose = compose;
};
Both examples are shorten, but the point is that require is used in the first time, while not used in the second time.
The issue is that I am not able to run the version using require in the browser due to this error
ReferenceError: require is not defined
When I included require.js into page, I got this error
Error: Module name "../Prelude" has not been loaded yet for context: _. Use require([])
http://requirejs.org/docs/errors.html#notloaded
Thus my question is, what can be done to run the first case in browser.
My guess would be that this comes from running builds with different --require-path options; once with the old default, which was an empty string, and once with ../. This would lead to psc-bundle not realising it needed to include Prelude and Control.Monad.Eff properly in the first case. psc-bundle should replace those require calls with references to the other modules, so that the code works in browsers.
There are a few different ways this can happen, and the compiler has been updated now in a way that should make the probability of this happening again much lower, so I wouldn't spend too much time worrying about exactly how this has occurred.
If none of the above makes any sense to you, don't worry; I think you just need to do the following to fix this:
Update to the latest version of psc (0.8.3 changed the --require-path default to ../, so any version after 0.8.3 should do, but you will want the latest version in most cases)
Delete your output directory
Compile everything again.
You probably need to use the --browserify option to build the first case for the browser.

Resources