Title: Validation fails when running via JsDom in NodeJs · Issue #5151 · plotly/plotly.js · GitHub
Open Graph Title: Validation fails when running via JsDom in NodeJs · Issue #5151 · plotly/plotly.js
X Title: Validation fails when running via JsDom in NodeJs · Issue #5151 · plotly/plotly.js
Description: Dear, we are integrating the powerful Plotly library into the open-source Node-RED iot framework, which is running on NodeJs. Running Plotly on the NodeJs server (via JsDom) works fine, but calling the validate function fails. A small pr...
Open Graph Description: Dear, we are integrating the powerful Plotly library into the open-source Node-RED iot framework, which is running on NodeJs. Running Plotly on the NodeJs server (via JsDom) works fine, but calling...
X Description: Dear, we are integrating the powerful Plotly library into the open-source Node-RED iot framework, which is running on NodeJs. Running Plotly on the NodeJs server (via JsDom) works fine, but calling...
Opengraph URL: https://github.com/plotly/plotly.js/issues/5151
X: @github
Domain: github.com
{"@context":"https://schema.org","@type":"DiscussionForumPosting","headline":"Validation fails when running via JsDom in NodeJs","articleBody":"Dear,\r\n\r\nwe are integrating the powerful Plotly library into the open-source Node-RED iot framework, which is running on NodeJs.\r\n\r\nRunning Plotly on the NodeJs server (via JsDom) works fine, but calling the `validate` function fails.\r\nA small program to reproduce the problem (remark: the jsdom and plotly.js-dist NPM packages need to be installed):\r\n```\r\nconst jsdom = require('jsdom');\r\nconst vm = require('vm'); \r\nconst fs = require('fs');\r\n\r\nvar plotlyServerDom = new jsdom.JSDOM('', { runScripts: 'dangerously'});\r\n \r\n// Mock a few things that JSDOM doesn't support out-of-the-box\r\nplotlyServerDom.window.HTMLCanvasElement.prototype.getContext = function() { return null; };\r\nplotlyServerDom.window.URL.createObjectURL = function() { return null; };\r\n\r\n// Run Plotly inside Jsdom\r\nvar plotlyJsPath = require.resolve(\"plotly.js-dist\");\r\nvar plotlyJsSource = fs.readFileSync(plotlyJsPath, 'utf-8');\r\nplotlyServerDom.window.eval(plotlyJsSource);\r\n\r\nvar data = [];\r\nvar trace ={};\r\ntrace.name = \"mytrace\"; \r\ntrace.type = \"scatter\";\r\ntrace.x = [\"2020-09-06 09:10:49\"];\r\ntrace.y = [5];\r\ntrace.opacity = 1;\r\ndata.push(trace);\r\n\r\nvar layout = {};\r\nlayout.title = {};\r\nlayout.title.text = \"mytitle\";\r\n\r\nvar result = plotlyServerDom.window.Plotly.validate(data, layout);\r\n\r\nif (result) {\r\n console.log(result);\r\n}\r\nelse {\r\n console.log(\"Validation ok\");\r\n}\r\n```\r\nThe result is:\r\n\u003e[\r\n {\r\n code: 'object',\r\n container: 'layout',\r\n trace: null,\r\n path: '',\r\n astr: '',\r\n msg: 'The layout argument must be linked to an object container'\r\n },\r\n {\r\n code: 'object',\r\n container: 'data',\r\n trace: 0,\r\n path: '',\r\n astr: '',\r\n msg: 'Trace 0 in the data argument must be linked to an object container'\r\n }\r\n]\r\n\r\nWhich means that Plotly doesn't recognize my input parameters as Javascript objects, which is checked in the [isPlainObject ](https://github.com/plotly/plotly.js/blob/master/src/lib/is_plain_object.js#L23) function:\r\n\r\n\r\n\r\nThe second check fails, so the input is not considered as a Javascript object...\r\nSeems that the problem is caused by this:\r\n1. JsDom runs its own vm (virtual machine) in NodeJs.\r\n2. That vm gets its very own instance of Object. \r\n3. The prototype of an object created inside the vm will not be the exact equal to the prototype of the Object outside the vm (i.e. in my program).\r\n4. So the equality in the IF condition will fail ...\r\n\r\nYou can find a prove of this theory in the next program. Here a global function `createObject` is declared inside the vm, and called from outside the vm. This allows us to create an empty Javascript object inside the vm, return it back to us so we can add data to the object, and then we pass the object to Plotly (which is running in the Jsdom vm):\r\n```\r\nconst jsdom = require('jsdom');\r\nconst vm = require('vm'); \r\nconst fs = require('fs');\r\n\r\nvar plotlyServerDom = new jsdom.JSDOM('', { runScripts: 'dangerously'});\r\n \r\n// Mock a few things that JSDOM doesn't support out-of-the-box\r\nplotlyServerDom.window.HTMLCanvasElement.prototype.getContext = function() { return null; };\r\nplotlyServerDom.window.URL.createObjectURL = function() { return null; };\r\n \r\n// Script to add global functions (to create a Javascript object) in the server DOM context\r\nconst script = new vm.Script(`\r\n function createObject() {\r\n return {};\r\n }\r\n`);\r\n\r\n// Execute the script in the VM (of jsDom), so that the global function is added to the VM context\r\nconst serverDomVmContext = plotlyServerDom.getInternalVMContext();\r\nscript.runInContext(serverDomVmContext);\r\n \r\nvar plotlyJsPath = require.resolve(\"plotly.js-dist\");\r\nvar plotlyJsSource = fs.readFileSync(plotlyJsPath, 'utf-8');\r\nplotlyServerDom.window.eval(plotlyJsSource);\r\n\r\nvar data = [];\r\nvar trace = plotlyServerDom.window.createObject();\r\ntrace.name = \"mytrace\"; \r\ntrace.type = \"scatter\";\r\ntrace.x = [\"2020-09-06 09:10:49\"];\r\ntrace.y = [5];\r\ntrace.opacity = 1;\r\ndata.push(trace);\r\n\r\nvar layout = plotlyServerDom.window.createObject();\r\nlayout.title = plotlyServerDom.window.createObject();\r\nlayout.title.text = \"mytitle\";\r\n\r\nvar result = plotlyServerDom.window.Plotly.validate(data, layout);\r\n\r\nif (result) {\r\n console.log(result);\r\n}\r\nelse {\r\n console.log(\"Validation ok\");\r\n}\r\n```\r\nAnd that works fine, and the validation is succesful.\r\n\r\nWe could use this latter program as a workaround, but it becomes harder when the number of nested levels increases. Because all objects that we receive from anywhere in the NodeJs application, we need to create an new object for and copy all the properties. Which is of course not a very neat solution ...\r\n\r\nSo we would like to ask if it is possible to remove the second condition from the IF statement, since we think that the first condition is enough:\r\n\r\n\r\n\r\nThanks for your time!\r\nBart Butenaers\r\n\r\n","author":{"url":"https://github.com/bartbutenaers","@type":"Person","name":"bartbutenaers"},"datePublished":"2020-09-15T21:54:09.000Z","interactionStatistic":{"@type":"InteractionCounter","interactionType":"https://schema.org/CommentAction","userInteractionCount":5},"url":"https://github.com/5151/plotly.js/issues/5151"}
| route-pattern | /_view_fragments/issues/show/:user_id/:repository/:id/issue_layout(.:format) |
| route-controller | voltron_issues_fragments |
| route-action | issue_layout |
| fetch-nonce | v2:3ae35ef1-5bae-e772-cddd-b2bae1543137 |
| current-catalog-service-hash | 81bb79d38c15960b92d99bca9288a9108c7a47b18f2423d0f6438c5b7bcd2114 |
| request-id | C116:208386:34310C:45855B:6A5B2324 |
| html-safe-nonce | bc8e7d84e2267037528e2fce8f155f3b3c4676976b2ac86794b8ba368d8afe8a |
| visitor-payload | eyJyZWZlcnJlciI6IiIsInJlcXVlc3RfaWQiOiJDMTE2OjIwODM4NjozNDMxMEM6NDU4NTVCOjZBNUIyMzI0IiwidmlzaXRvcl9pZCI6IjYzOTU2OTY4NzY2OTI5MDY3ODgiLCJyZWdpb25fZWRnZSI6ImlhZCIsInJlZ2lvbl9yZW5kZXIiOiJpYWQifQ== |
| visitor-hmac | bfbf01d4ddfb2f234a6ded6b105fc3813bfa4a02e45fc354b5aa0041da04804e |
| hovercard-subject-tag | issue:702299358 |
| github-keyboard-shortcuts | repository,issues,copilot |
| google-site-verification | Apib7-x98H0j5cPqHWwSMm6dNU4GmODRoqxLiDzdx9I |
| octolytics-url | https://collector.github.com/github/collect |
| analytics-location | / |
| fb:app_id | 1401488693436528 |
| apple-itunes-app | app-id=1477376905, app-argument=https://github.com/_view_fragments/issues/show/plotly/plotly.js/5151/issue_layout |
| twitter:image | https://opengraph.githubassets.com/86294ab953021da2209a7430bbd9e82fb0ac333b899ea84cd4f2bdbe1143cfbf/plotly/plotly.js/issues/5151 |
| twitter:card | summary_large_image |
| og:image | https://opengraph.githubassets.com/86294ab953021da2209a7430bbd9e82fb0ac333b899ea84cd4f2bdbe1143cfbf/plotly/plotly.js/issues/5151 |
| og:image:alt | Dear, we are integrating the powerful Plotly library into the open-source Node-RED iot framework, which is running on NodeJs. Running Plotly on the NodeJs server (via JsDom) works fine, but calling... |
| og:image:width | 1200 |
| og:image:height | 600 |
| og:site_name | GitHub |
| og:type | object |
| og:author:username | bartbutenaers |
| hostname | github.com |
| expected-hostname | github.com |
| None | 5290d7e14309ad1e76106a9c4237bd1041517e83ea182c8ab756752cb0c6940b |
| turbo-cache-control | no-preview |
| go-import | github.com/plotly/plotly.js git https://github.com/plotly/plotly.js.git |
| octolytics-dimension-user_id | 5997976 |
| octolytics-dimension-user_login | plotly |
| octolytics-dimension-repository_id | 45646037 |
| octolytics-dimension-repository_nwo | plotly/plotly.js |
| octolytics-dimension-repository_public | true |
| octolytics-dimension-repository_is_fork | false |
| octolytics-dimension-repository_network_root_id | 45646037 |
| octolytics-dimension-repository_network_root_nwo | plotly/plotly.js |
| turbo-body-classes | logged-out env-production page-responsive |
| disable-turbo | false |
| browser-stats-url | https://api.github.com/_private/browser/stats |
| browser-errors-url | https://api.github.com/_private/browser/errors |
| release | 9c975978430e9ad293956f2bbdaf153b1bd84a99 |
| ui-target | full |
| theme-color | #1e2327 |
| color-scheme | light dark |
Links:
Viewport: width=device-width