-
Notifications
You must be signed in to change notification settings - Fork 1.5k
Conversation
This is the next escalation on the war against the Travis unit tests failing (which came into being by yours truly). By accident, I could recreate the unit test failure on my development machine. This led to a more directed effort to squash the bug. The insight here is that test `(window === undefined)` fails, but `(typeof window === 'undefined`)` succeeds. This undoubtedly has to do with the special status `window` has as a global object. Changes: - Added check on presence of `window` in `Canvas._requestNextFrame()`, fixed local source errors. - Added catch clause in `CanvasRendered._determinePixelRatio()` - small fix: raised timeout for the network `worldCup2014` unit test
- Small fixes and cleanup in `util.js` - Removed `util.protoExtend()`, not used anywhere
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Really minor changes.
All in all, really good job! Cleans up the code a whole lot!
lib/network/modules/Groups.js
Outdated
*/ | ||
get(groupname) { | ||
get(groupname, create = true) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I personally like more declarative variables. I think it should be: create
=> shouldCreate
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
var Node = require("./components/Node").default; | ||
var Label = require("./components/shared/Label").default; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
extra line
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You mean that there are two empty lines? I actually prefer this, for better visual separation. I do it between methods as well, and in other places where I think a bigger distinction between blocks is useful.
* @param {MultiName} multiName sub path for the mod-font | ||
* @returns {ModOptions} | ||
*/ | ||
var getFontOptions = (multiName) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do you use var
? Why not use const
and let
?
(for consistency)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A quick response on this one in passing:
I prefer var
because chrome has issues dealing with const
and let
, it can't seem to display the values correctly inline (usually shows undefined
even though it has a value). I find this bothersome to the point of forsaking them, even though let
would be better.
If you have a solution for this, let me know.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But it seems that you douse let
in the rest of the file. How does that work?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I really try to use let
. But if it gets in the way of debugging, I revert it to var
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh well, I can change this since I'm done with debugging.
(I hope)
Update: Did var
=> let
for `Label.js.
|
||
var fontString = ""; | ||
if (values.mod !== undefined && values.mod !== "") { // safeguard for undefined - this happened | ||
fontString += values.mod = " "; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think you mean fontString += values.mod + " "
? It seems really weird to me if you meant to assign values.mod = " "
and simultaneously add a space to fontString
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Indeed. Good catch.
lib/network/modules/NodesHandler.js
Outdated
@@ -144,7 +142,9 @@ class NodesHandler { | |||
throw 'Internal error: mass in defaultOptions of NodesHandler may not be zero or negative'; | |||
} | |||
|
|||
util.extend(this.options, this.defaultOptions); | |||
//this.options = {}; | |||
//util.extend(this.options, this.defaultOptions); // Candidate for the Hall of Shame! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why keep the dead code?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Heh. For the epitaph, actually. Major stupidity was performed here. I do this often in my own code, so that I remember not to do that thing every time I encounter the comment.
But this is shared code, and keeping it in is just vanity. Hereby gone.
@@ -2,11 +2,24 @@ let util = require('../../../../util'); | |||
let ComponentUtil = require('./ComponentUtil').default; | |||
let LabelSplitter = require('./LabelSplitter').default; | |||
|
|||
/** | |||
* @typedef {'bold'|'ital'|'boldital'|'mono'|'normal'} MultiName | |||
* |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A better name for this type might be FontStyle
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, although I do want to convey that this is the list of names of special fonts that can be used when multi-font is enabled.
Will you settle for MultiFontStyle
?
*/ | ||
|
||
/** | ||
* @typedef {{color:string, size:number, face:string, mod:string, vadjust:number}} ModOptions |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Suggest MultiFontOptions
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
// | ||
// Main body of the method | ||
// | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this main body comment block can be dropped
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ok. The idea was to indicate where the actual function code began, after the definitions of the local function. But I just moved them away, so....
* Member fontOptions serves as an accumulator for the current font options. | ||
* As such, it needs to be completely separated from the node options. | ||
* | ||
* @param {Object} options |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this the ModOptions from above, or other options?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Other options, whatever the user specified or otherwise the defaults.
if (!this.fontOptions.multi) return; | ||
propagateFonts(pile) { | ||
let fontPile = []; // sequence of font objects to consider, order important | ||
let mods = ['bold', 'ital', 'boldital', 'mono']; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks like a good candidate for a top level constant, rather than re initialized every time propagateFonts()
is called.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
* | ||
* @returns {object} object with all current own basic font properties | ||
*/ | ||
let getBasicOptions = () => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm seeing a lot of nested function calls and wondering if it is a micro optimization to bring them up a level, so that they're initialized with the class, rather than initialized with every call to the function that encapsulates them.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK. My long-term planning is to isolate this pile handling code in a separate class. This is the approach that I want to use to replace the prototype chaining in options. I thought it was too early to make a class of it now.
But fair enough, I can define them as private methods for the time being. Wrt. 'micro optimization', it's an easy task to raise them to class-level, and every teeny bit helps.
test/Label.test.js
Outdated
|
||
assert.equal(modBold(0).color, 'black'); // nodes default | ||
assert.equal(modBold(1).color, 'black'); // more specific bold value overrides group value | ||
assert.equal(modBold(2).color, 'black'); // idem |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Idem?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why are these three changed, and the others aren't? I'm missing something here...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
'Idem' : Same as previous comment in this column.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Overview:
- The priority of the options is:
node, group, default node, global default
. black
is in 'default node'.- multi-font options, if defined, take precedence. The 'normal' font options are fall-backs for the multi-font options.
Specific:
- Node 0 has no 'node' or 'group' options, hence uses 'default node'.
- Nodes 1 has
group2
, which has a default font but no explicit bold font. The bold definition in 'default node' trumps this. - Node 2 has
group1
, the same logic as with Node 1 applies here. - Nodes 3 and 4 have bold fonts defined in the node instance. These trump everything.
Welcome to the wonderful world of multi-font options. Precisely this kind of thing is what made fixing the issue so hard. My head still hurts.
test/Label.test.js
Outdated
// | ||
// Same initialization as previous with a color set for the default node font | ||
// | ||
data.nodes = new vis.DataSet(dataNodes); // Need to reset nodes, changed in previous |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This isn't really a reset is it?
It's a new dataset, but the since dataNodes is a mutable array of mutable objects, and not a function who's return value is a new array of objects, the nodes have not been reset.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, but dataNodes
is not changed anywhere, so the same original values are being entered in the DataSet
instance, and DataSet
copies values internally[*].
Crap, let me confirm that.
Update: Confirmed with added assertions, nodes dataset is reset to initial values. You had me worried for a moment.
[*] Code check: confirmed. true for non-object options, can't tell for sure for object options, but it does work as described.
|
||
done(); | ||
}); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great addition of tests! Well done!
I would suggest, however, refactoring these very large tests into a number of much smaller tests, in separate test classes where required, and making use of before
and after
to set up the state you need.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess; they're kind of lengthy, that's true. I'll have a think about how to carve this up.
Update: See commit; this is as far as I wish/dare to take it. I hope this is satisfactory for you.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Excellent! The only other suggestion I'll make is to follow the mocha convention for pending
, or write the entire test and use skip
, as described here: https://mochajs.org
This is a nit pick, and I'm approving as-is, with or without my suggestion.
@mbroad review 👍, you're being sharp. |
* List of special styles for multi-fonts | ||
* @private | ||
*/ | ||
const multiFontStyle = ['bold', 'ital', 'boldital', 'mono']; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
normal
is omitted?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah. this array is the list of 'special' fonts. Style 'normal' would be the catch-all default font, i.e. not special. It should not be in this particular list.
|
||
done(); | ||
}); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Excellent! The only other suggestion I'll make is to follow the mocha convention for pending
, or write the entire test and use skip
, as described here: https://mochajs.org
This is a nit pick, and I'm approving as-is, with or without my suggestion.
OK, I think this is a minor thing. I will leave it as is, but thanks for pointing it out. |
@justinharrel, since your comment on |
This fixes an oversight in almende#3486. Unit tests added, not only for null labels, but for all weird label values I could thing of.
* Network: Add extra check on null value during label handling This fixes an oversight in #3486. Unit tests added, not only for null labels, but for all weird label values I could thing of. * Enhanced unit tests, adjusted label check
I accidentally commented on commit rather than PR, its still there. widthConstraint still does not seem to work. Should I just make a new issue? |
What another failing case? Sure, make a new issue. If you can give a test-case that would be welcome. |
* The next fix on Travis unit test failure This is the next escalation on the war against the Travis unit tests failing (which came into being by yours truly). By accident, I could recreate the unit test failure on my development machine. This led to a more directed effort to squash the bug. The insight here is that test `(window === undefined)` fails, but `(typeof window === 'undefined`)` succeeds. This undoubtedly has to do with the special status `window` has as a global object. Changes: - Added check on presence of `window` in `Canvas._requestNextFrame()`, fixed local source errors. - Added catch clause in `CanvasRendered._determinePixelRatio()` - small fix: raised timeout for the network `worldCup2014` unit test * Preliminary refactoring in utils.js * Added unit tests for extend routines, commenting and small fixes * More unit tests for extend routines * - Completed unit tests for extend routines in - Small fixes and cleanup in `util.js` - Removed `util.protoExtend()`, not used anywhere * Added unit tests for known font options * Interim save before trying out another proto chain strategy * Fixed problem in first example almende#3408 * Removed silly file that shouldn't be there * Added unit test for multi-fonts * Comment edits * Verufy unit tests, small adjustments for groups * Further work on getting unit tests to work. PARTS NEED TO BE CLEANED UP! * Further tweaks to get unit tests working * All unit tests passing * Fixes due to linting * Small edits * Removed prototype handling from font pile * Fixes during testing examples of almende#3408 * Added unit test for edge labels, small fixes * Added unit tests for shorthand string fonts; some tests still failing * All unit tests pass * Removed Label.parseOptions() * Completed shorthand font tests, code cleanup, fixed choosify for edges * Addressed review comments * Addressed review comments, cleanup
…#3511) * Network: Add extra check on null value during label handling This fixes an oversight in almende#3486. Unit tests added, not only for null labels, but for all weird label values I could thing of. * Enhanced unit tests, adjusted label check
via @macleodbroad-wf, @wimrijnders (almende/vis#3228, almende/vis#3470, almende/vis#3486, almende/vis#3511, almende#3520) , almende#3518, almende#3575, almende#3565, almende#3603, almende#3646)
Fixes #3408, #2677.
This PR serves to get the handling of the font options right. The fixing of the issue is more or less a side-effect. As a special note, with this PR the group options for fonts are finally handled correctly.
Guiding principles:
node, group, default node, global default
.Changes
Got rid of
Label#parseOptions()
. This changes the option values, and we want to keep them asspecified.
Adjusted usage of groups, at least for the font options in groups.
Group font options were merged into the options of a given node instance, and the label
handling requires these to be separate. In fact, it is questionable if the group options
should be merged into the node instance options at all.
Unit tests for the setting of font options in all manners have been added.
Special Note: The added unit tests picked up an unrelated bug in
util.deepExtend()
andutil.selectiveDeepExtend()
; added an extra check onnull
there.Further changes
edgeOptions
in moduleEdgesHandler
, it's just a reference tothis.options
nodeOptions
in moduleNodesHandler
, it was useless for the same reasonLabel.propagateFonts()
, additional notes :mono
has been dropped; if users want tohave a non-proportional font for
mono
, that's their problem.ratio
has been dropped. This does not have any effect on the outcome. The value ofratio
was always1
in all the cases I have conceived to test.Final Comment
If anything is to be concluded from this fix, it's that the option setup and handling within
Network
is unnecessarily complicated. This urgently needs to be looked at.In particular, the prototype chaining of options appears to be deficient. While the idea is
elegant in itself, the prototype order is not sufficient to handle all option sequences; in fact,
the code is rife with exceptions on it. Prototype chaining needs to be replaced with something
probably less elegant but better suited to the task.