Approach to unit-testing large batches of dynamically generated strings - javascript

My web app has a class for analysing and manipulating the data stored in the URL's hash, which look something like
http://myapp.com/#!/location/hornsea/season/spring/facilities/+shop+swimming-airport/size/50
To be able to unit test the various permutations I'm storing some URL fragments in an object (e.g.
var fragments {
valid: ["/location/hornsea", "/season/winter","/size/50"],
invalid: ["location/hornsea", "/seasonwinter","/size/fifty"]
}
And then before runing my tests I dynamically build all possible valid and invalid urls and then loop through these, adding a test for each url.
This is fine when I'm running a test for validity of the url, as I just check for true or false in my isValid() method, but when testing for getting the parameters from the URL I don't know how to approach the problem - I have to compare the returned value (e.g. {location: "hornsea""}) with the expected value, but as the string to be analysed is dynamically constructed before running the test I don't have the expected value stored anywhere.
Now, I'm not sure if my approach is overkill - should I care about testing all methods on all possible url structures? Could I set up tests that run against all possible urls when it's easy to do so, but run against a more manageable subset when I need to have finer control over the tests, and yet still manage to cover all eventualities. i.e. could I write a test suite where testing
http://myapp.com/season/autumn
is adequate enough to cover examples such as the following too.
http://myapp.com/location/dungeness/season/autumn
http://myapp.com/location/camberwell/seasonautumn
And how could I be sure that I haven't left any gaps? Are there any general approaches to this sort of problem, or is it something very specific to the particular application?

You can test this with an inverse function test. You have to write a function (encode) that's the inverse function to your URL parser. The encode function constructs the URL from the valid input values. The test has the form:
input = {"location" : "hornsea", "season" : "winter", "size" : "50"}
assert input == parse(encode(input))
To get a good best coverage you can use one of the Quickcheck implementations to generate the input values. With generated values you can create subsets of the valid input parameter or shuffle parameters.

Related

How can I identify potential parameters within a path from a list of URLs?

I am trying to do a HAR to OpenAPI spec conversion, and as part of this process, I'd like to be able to identify which URLs can be consolidated and replaced with path parameters.
Whilst I understand it may not be 100% accurate because all APIs are different, it would be good to automate a good amount and then allow the user to manually override afterwards if necessary.
Example Input
[
"/api/v1/users",
"/avatars/jane.doe",
"/avatars/joe.bloggs",
"/sounds/bing",
"/sounds/bong",
"/api/v1/users/jane.doe/profile",
"/api/v1/users/joe.bloggs/profile",
"/api/v1/parent/one/child/one",
"/api/v1/parent/one/child/two",
"/api/v1/parent/two/child/one"
]
Expected Output
[
"/api/v1/users",
"/avatars/:param1",
"/sounds/:param1",
"/api/v1/users/:param1/profile",
"/api/v1/parent/:param1/child/:param2"
]
Bonus points for also naming the parameter based on the segment before the parameter.
e.g.
[
"/api/v1/users",
"/avatars/:avatar",
"/sounds/:sound",
"/api/v1/users/:user/profile",
"/api/v1/parent/:parent/child/:child"
]
I've tried using the following Python library but this also attempts to fit Regular Expressions into the URLs which is more than what I am looking for.
https://pypi.org/project/os-urlpattern/
Ideally the solution will be provided in Javascript/Typescript.

Verifying the order/make up of URL, when you have the elements extracted from elsewhere

I want to verify the order of a URL that is generated from faceted navigation on a development website.
EG http://EcomWebsite.com/region/s/element1/element2/element3
As part of a different test step I have extracted the strings I needed and I can test these are present in the url. I've been doing this as
assertThat(displayShopPage.getCurrentURL(),containsString(displayShopPage.element1()));
assertThat(displayShopPage.getCurrentURL(),containsString(displayShopPage.element2()));
assertThat(displayShopPage.getCurrentURL(),containsString(displayShopPage.element3()));
but I mention I need to check the order of the URL.
The step in question is
"Then the order in which the values are included in the URL must be same order the values have within the facet"
I'm still rather new and raw to Java, Selenium, and BDD so I thank you for any help you peeps can provide.
EDIT:
I'm trying to avoiding hard coded url's and what I want to check in the URL for several reasons.
Element1 etc come from class's that extract the text from faceted navigation element options.
So if I hard code the URL or the text from the element options if the product data changes and so the faceted navigation it will break the test.
Hence why I'm extracting the string from the elements to compare against the URL rather than just checking for the URL it self.
getCurrentURL() returns a string, right? Why not simply compose the expected URL as a string then compare the strings?
currentUrl = displayShopPage.getCurrentURL()
expectedUrl = "http://EcomWebsite.com/" + displayShopPage.CF_PanelText() + "/s/" + displayShopPage.FacetGroup2OptionPanelText() # compose this from your page source...it isn't clear above what the component order needs to be
assertEquals(currentUrl, expectedUrl) # I made this up, I don't know the proper syntax for comparing strings

Are single-lettered word models supported in EmberJS?

I cannot really pinpoint whether the issue is with Ember or Ember data, or if it's even an issue, but here's what happens:
Let's say you've got your model called tell_me_a_story. This will be the name that your JSON should provide, should you be using ActiveModelAdapter.
Regardless, when Ember or Ember Data process it internally, it'll camelize it and it becomes tellMeAStory, correctly indicating that "A" and "Story" are two separate words.
However, when internally it is decamelized to lookup for the model, the decamelize function will convert it into tell_me_astory.
This final behavior seems flawed to me, but when looking at the tests that derived this behavior, it is actually intended to manage acronyms in that fashion. (Compare the following example with the "innerHtml" that I would expect for camel casing multi-letter acronyms.)
QUnit.test("converts a camelized string into all lower case separated by underscores.", function() {
deepEqual(decamelize('innerHTML'), 'inner_html');
if (Ember.EXTEND_PROTOTYPES) {
deepEqual('innerHTML'.decamelize(), 'inner_html');
}
});
(Source in Ember Runtime)
So, which is the correct way to use single-letter words in models with Ember? Are they even supported?
Here's an example of what I'm trying to do:
// this comes from a separate data source, e.g. REST APIs
var myModelName = 'tell_me_a_story';
// this line will throw if the model file is named "tell-me-a-story"
if (!store.getById(myModelName, 1)) {
// this line will throw if the model file is named "tell-me-astory"
store.pushPayload(myModelName, myObject);
}
You can override the stores _normalizeTypeKey then alter the camelCase behaviour to become what you want (e.g. dasherized or just fix this one case).
You can also override the serialisers typeForRoot when going the other way - this lets you tell ember what the model key is (e.g. tellMeAStory) for a particular key in your data (e.g. tell_me_a_story).
It appears there is work underway to make everything work like the container does (which is dasherized)

How to handle indices in Neo4j server via javascript REST client?

I have data in a standalone Neo4j REST server, including an index of nodes. I want pure JavaScript client to connect to Neo4j and serve the formatted data to d3.js, a visualisation library built on Node.js.
JugglingDB is very popular, but the Neo4j implementation was done "wrong": https://github.com/1602/jugglingdb/issues/56
The next most popular option on github is: https://github.com/thingdom/node-neo4j
looking at the method definitions https://github.com/thingdom/node-neo4j/blob/develop/lib/GraphDatabase._coffee
I'm able to use "getNodeById: (id, _) ->"
> node1 = db.getNodeById(12, callback);
returns the output from the REST server, including node properties. Awesome.
I can't figure out how to use "getIndexedNodes: (index, property, value, _) ->"
> indexedNodes = db.getIndexedNodes:(index1, username, Homer, callback);
...
indexedNodes don't get defined. I've tried a few different combinations. No joy. How do I use this command?
Also, getIndexedNodes() requires a key-value pair. Is there any way to get all, or a subset of the items in the index without looping?
One of the authors/maintainers of node-neo4j here. =)
indexedNodes don't get defined. I've tried a few different combinations. No joy. How do I use this command?
Your example seems to have some syntax errors. Are index1, username and Homer variables defined elsewhere? Assuming not, i.e. assuming those are the actual index name, property name and value, they need to be quoted as string literals, e.g. 'index1', 'username' and 'Homer'. But you also have a colon right before the opening parenthesis that shouldn't be there. (That's what's causing the Node.js REPL to not understand your command.)
Then, note that indexedNodes should be undefined -- getIndexedNodes(), like most Node.js APIs, is asynchronous, so its return value is undefined. Hence the callback parameter.
You can see an example of how getIndexedNodes() is used in the sample node-neo4j-template app the README references:
https://github.com/aseemk/node-neo4j-template/blob/2012-03-01/models/user.js#L149-L160
Also, getIndexedNodes() requires a key-value pair. Is there any way to get all, or a subset of the items in the index without looping?
getIndexedNodes() does return all matching nodes, so there's no looping required. Getting a subset isn't supported by Neo4j's REST API directly, but you can achieve the result with Cypher.
E.g. to return the 6th-15th user (assuming they have a type property set to user) sorted alphabetically by username:
db.query([
'START node=node:index1(type="user")',
'RETURN node ORDER BY node.username',
'SKIP 5 LIMIT 10'
].join('\n'), callback);
Cypher is still rapidly evolving, though, so be sure to reference the documentation that matches the Neo4j version you're using.
As mentioned above, in general, take a look at the sample node-neo4j-template app. It covers a breadth of features that the library exposes and that a typical app would need.
Hope this helps. =)
Neo4j 2 lets you do indices VIA REST. Docs here
REST Indicies

javascript: array of object for simple localization

I need to implement a simple way to handle localization about weekdays' names, and I came up with the following structure:
var weekdaysLegend=new Array(
{'it-it':'Lunedì', 'en-us':'Monday'},
{'it-it':'Martedì', 'en-us':'Tuesday'},
{'it-it':'Mercoledì', 'en-us':'Wednesday'},
{'it-it':'Giovedì', 'en-us':'Thursday'},
{'it-it':'Venerdì', 'en-us':'Friday'},
{'it-it':'Sabato', 'en-us':'Saturday'},
{'it-it':'Domenica', 'en-us':'Sunday'}
);
I know I could implement something like an associative array (given the fact that I know that javascript does not provide associative arrays but objects with similar structure), but i need to iterate through the array using numeric indexes instead of labels.
So, I would like to handle this in a for cycle with particular values (like j-1 or indexes like that).
Is my structure correct? Provided a variable "lang" as one of the value between "it-it" or "en-us", I tried to print weekdaysLegend[j-1][lang] (or weekdaysLegend[j-1].lang, I think I tried everything!) but the results is [object Object]. Obviously I'm missing something..
Any idea?
The structure looks fine. You should be able to access values by:
weekdaysLegend[0]["en-us"]; // returns Monday
Of course this will also work for values in variables such as:
weekdaysLegend[i][lang];
for (var i = 0; i < weekdaysLegend.length; i++) {
alert(weekdaysLegend[i]["en-us"]);
}
This will alert the days of the week.
Sounds like you're doing everything correctly and the structure works for me as well.
Just a small note (I see the answer is already marked) as I am currently designing on a large application where I want to put locals into a javascript array.
Assumption: 1000 words x4 languages generates 'xx-xx' + the word itself...
Thats 1000 rows pr. language + the same 7 chars used for language alone = wasted bandwitdh...
the client/browser will have to PARSE THEM ALL before it can do any lookup in the arrays at all.
here is my approach:
Why not generate the javascript for one language at a time, if the user selects another language, just respond(send) the right javascript to the browser to include?
Either store a separate javascript with large array for each language OR use the language as parametre to the server-side script aka:
If the language file changes a lot or you need to minimize it per user/module, then its quite archivable with this approach as you can just add an extra parametre for "which part/module" to generate or a timestamp so the cache of the javascript file will work until changes occures.
if the dynamic approach is too performance heavy for the webserver, then publish/generate the files everytime there is a change/added a new locale - all you'll need is the "language linker" check in the top of the page, to check which language file to server the browser.
Conclusion
This approach will remove the overhead of a LOT of repeating "language" ID's if the locales list grows large.
You have to access an index from the array, and then a value by specifying a key from the object.
This works just fine for me: http://jsfiddle.net/98Sda/.
var day = 2;
var lang = 'en-us';
var weekdaysLegend = [
{'it-it':'Lunedì', 'en-us':'Monday'},
{'it-it':'Martedì', 'en-us':'Tuesday'},
{'it-it':'Mercoledì', 'en-us':'Wednesday'},
{'it-it':'Giovedì', 'en-us':'Thursday'},
{'it-it':'Venerdì', 'en-us':'Friday'},
{'it-it':'Sabato', 'en-us':'Saturday'},
{'it-it':'Domenica', 'en-us':'Sunday'}
];
alert(weekdaysLegend[day][lang]);

Categories