Skip to content

Commit

Permalink
Add Node.js REST server and client-side REST mock (#1470)
Browse files Browse the repository at this point in the history
dgrid's tests currently rely on a PHP script for REST testing. This change
adds a Node.js REST server and client-side REST mocking with
'dojo/request/registry'
  • Loading branch information
msssk authored May 22, 2020
1 parent 59b1a3e commit e5d31e8
Show file tree
Hide file tree
Showing 8 changed files with 566 additions and 174 deletions.
3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,5 +22,8 @@
},
"main": "./OnDemandGrid",
"icon": "http://packages.dojofoundation.org/images/dgrid.png",
"scripts": {
"test-server": "node ./test/data/rest-node.js"
},
"dojoBuild": "package.js"
}
284 changes: 152 additions & 132 deletions test/Rest.html
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@
@import "../css/dgrid.css";
@import "../css/skins/claro.css";

body {
padding-left: 2rem;
}

h2 {
margin: 12px;
}
Expand All @@ -22,149 +26,165 @@
margin: 10px;
}
</style>
<script src="../../dojo/dojo.js"
data-dojo-config="async: true"></script>
</head>

<body class="claro">
<div id="selectServerForm"></div>

<h2>A basic grid with Rest store</h2>
<div id="grid"></div>

<h2>A basic grid with Rest store using range headers</h2>
<div id="gridRangeHeaders"></div>

<script>
var dojoConfig = {
async: true,
requestProvider: 'dojo/request/registry'
};
</script>
<script src="../../dojo/dojo.js"></script>
<script>
require([
"dojo/_base/lang",
"dgrid/List",
"dgrid/OnDemandGrid",
"dgrid/Selection",
"dgrid/Editor",
"dgrid/Keyboard",
"dgrid/Tree",
"dojo/_base/declare",
"dojo/query",
"dstore/Rest",
"dstore/Trackable",
"dstore/Cache",
"dstore/Tree",
"dojo/domReady!"
], function (lang, List, Grid, Selection, Editor, Keyboard, Tree, declare, query, Rest, Trackable, Cache, TreeStore) {

var CustomGrid = declare([Grid, Selection, Keyboard, Editor, Tree], {
insertRow: function () {
refreshed = true;
return this.inherited(arguments);
},

removeRow: function () {
refreshed = true;
return this.inherited(arguments);
},

logPreload: function () {
var line = '';
for (var i = 0; i < 160; i++) {
line += '\u2500';
}
console.log(line);
var preload = this.preload;
if (preload) {
while (preload.previous) {
preload = preload.previous;
'dojo/_base/lang',
'dojo/Deferred',
'dojo/query',
'dgrid/List',
'dgrid/OnDemandGrid',
'dgrid/Selection',
'dgrid/Editor',
'dgrid/Keyboard',
'dgrid/Tree',
'dgrid/test/widgets/SelectServer',
'dstore/Rest',
'dstore/Trackable',
'dstore/Cache',
'dstore/Tree'
], function (lang, Deferred, query, List, Grid, Selection, Editor, Keyboard, Tree,
SelectServer, Rest, Trackable, Cache, TreeStore) {

var selectServer = new SelectServer({}, document.getElementById('selectServerForm'));

selectServer.startup();
selectServer.startupPromise.then(function () {
var CustomGrid = Grid.createSubclass([ Selection, Keyboard, Editor, Tree ], {
insertRow: function () {
refreshed = true;
return this.inherited(arguments);
},

removeRow: function () {
refreshed = true;
return this.inherited(arguments);
},

logPreload: function () {
var line = '';
for (var i = 0; i < 160; i++) {
line += '\u2500';
}
var preloads = [];
var preloadNodes = [];
var height = 0;
var totalPossibleRows = 0;
while (preload) {
preloads.push(preload);
var node = preload.node;
height += node.offsetHeight;
totalPossibleRows += preload.count;
preloadNodes.push({
preloadId: node.getAttribute('data-preloadid'),
rowIndex: node.rowIndex,
height: node.style.height,
offsetTop: node.offsetTop,
offsetHeight: node.offsetHeight
});
preload = preload.next;
console.log(line);
var preload = this.preload;
if (preload) {
while (preload.previous) {
preload = preload.previous;
}
var preloads = [];
var preloadNodes = [];
var height = 0;
var totalPossibleRows = 0;
while (preload) {
preloads.push(preload);
var node = preload.node;
height += node.offsetHeight;
totalPossibleRows += preload.count;
preloadNodes.push({
preloadId: node.getAttribute('data-preloadid'),
rowIndex: node.rowIndex,
height: node.style.height,
offsetTop: node.offsetTop,
offsetHeight: node.offsetHeight
});
preload = preload.next;
}
console.table && console.table(preloads);
console.table && console.table(preloadNodes);
var realRowCount = query('.dgrid-row', grid.contentNode).length;
height += 24 * realRowCount;
totalPossibleRows += realRowCount;
console.log('Height calculated from preloads = ', height);


console.log('Actual grid content height = ', gridContentHeight());
console.log('Total possible rows = ', totalPossibleRows);
console.log('Current row count = ', realRowCount);
}
}
});

function createStore(config) {
var store = new (Rest.createSubclass([ Trackable, Cache, TreeStore ]))(lang.mixin({
target: selectServer.get('targetUrl'),
put: function (object) {
var dfd = new Deferred();
dfd.resolve(object);
return dfd.promise;
}
console.table(preloads);
console.table(preloadNodes);
var realRowCount = query('.dgrid-row', grid.contentNode).length;
height += 24 * realRowCount;
totalPossibleRows += realRowCount;
console.log('Height calculated from preloads = ', height);
}, config));

store.getRootCollection = function () {
return this.root.filter({ parent: undefined });
};

console.log('Actual grid content height = ', gridContentHeight());
console.log('Total possible rows = ', totalPossibleRows);
console.log('Current row count = ', realRowCount);
}
return store;
}
});

function createStore(config) {
var store = new declare([Rest, Trackable, Cache, TreeStore])(lang.mixin({
target: "./data/rest.php",
put: function (object) {
return object;
}
}, config));

store.getRootCollection = function () {
return this.root.filter({ parent: undefined });
};

return store;
}

function getColumns() {
return [
{ label: 'Name', field: 'name', sortable: false, renderExpando: true },
{ label: 'Id', field: 'id' },
{ label: 'Comment', field: 'comment', sortable: false, editor: "text" },
{ label: 'Boolean', field: 'boo', sortable: false, autoSave: true, editor: "checkbox" }
];
}

window.grid = new CustomGrid({
pagingMethod: 'throttleDelayed',
collection: createStore().getRootCollection(),
sort: "id",
getBeforePut: false,
columns: getColumns()
}, "grid");

var refreshed = false;
var prevContentHeight = 0;
setInterval(function () {
var contentHeight = gridContentHeight();
if (refreshed || prevContentHeight != contentHeight) {
prevContentHeight = contentHeight;
refreshed = false;
grid.logPreload();
function getColumns() {
return [
{ label: 'Name', field: 'name', sortable: false, renderExpando: true },
{ label: 'Id', field: 'id' },
{ label: 'Comment', field: 'comment', sortable: false, editor: 'text' },
{ label: 'Boolean', field: 'boo', sortable: false, autoSave: true, editor: 'checkbox' }
];
}
}, 1500);

function gridContentHeight() {
var contentHeight = 0;
var childNodes = grid.contentNode.childNodes;
var len = childNodes.length;
for (var i = 0; i < len; i++) {
contentHeight += childNodes[i].offsetHeight;

window.grid = new CustomGrid({
pagingMethod: 'throttleDelayed',
collection: createStore().getRootCollection(),
sort: 'id',
getBeforePut: false,
columns: getColumns()
}, 'grid');

var refreshed = false;
var prevContentHeight = 0;
setInterval(function () {
var contentHeight = gridContentHeight();
if (refreshed || prevContentHeight != contentHeight) {
prevContentHeight = contentHeight;
refreshed = false;
grid.logPreload();
}
}, 1500);

function gridContentHeight() {
var contentHeight = 0;
var childNodes = grid.contentNode.childNodes;
var len = childNodes.length;
for (var i = 0; i < len; i++) {
contentHeight += childNodes[i].offsetHeight;
}
return contentHeight;
}
return contentHeight;
}

new CustomGrid({
collection: createStore({ useRangeHeaders: true }).getRootCollection(),
sort: "id",
getBeforePut: false,
columns: getColumns()
}, "gridRangeHeaders");
});

new CustomGrid({
collection: createStore({ useRangeHeaders: true }).getRootCollection(),
sort: 'id',
getBeforePut: false,
columns: getColumns()
}, 'gridRangeHeaders');
});
});
</script>
</head>
<body class="claro">
<h2>A basic grid with Rest store</h2>
<div id="grid"></div>

<h2>A basic grid with Rest store using range headers</h2>
<div id="gridRangeHeaders"></div>
</body>
</html>
66 changes: 66 additions & 0 deletions test/data/rest-node.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
/*
A simple test server that returns dynamic data.
Sort: not supported
Paging: supports both "limit(count,start)" in the querystring and Range/X-Range header: items=start-end
Hierarchical data: supports the "parent" parameter in the querystring
See: https://github.com/SitePen/dstore/blob/master/docs/Stores.md#request
Launch command: node rest-node.js
Stop server with Ctrl+C
*/

var http = require('http');
var process = require('process');
var url = require('url');
var restHelpers = require('./restHelpers');

var PORT_NUMBER = 8040;
var RESPONSE_DELAY_MIN = 20;
var RESPONSE_DELAY_MAX = 100;

var server = http.createServer(function (request, response) {
var requestUrl = new url.URL(request.url, 'http://' + request.headers.host);
var searchParams = {};
requestUrl.searchParams.forEach(function (value, name) {
searchParams[name] = value;
});
var responseData = restHelpers.getResponseData(searchParams, request.headers);
var responseDelay = restHelpers.getDelay(RESPONSE_DELAY_MIN, RESPONSE_DELAY_MAX);

response.setHeader('Access-Control-Allow-Headers', '*, Range, X-Range, X-Requested-With');
response.setHeader('Access-Control-Allow-Origin', '*');
response.setHeader('Access-Control-Expose-Headers', 'Content-Range');
response.setHeader('Content-Type', 'application/json');
response.setHeader('Content-Range', responseData.contentRange);
response.write(JSON.stringify(responseData.items));

if (responseDelay) {
setTimeout(function () {
response.end();
}, responseDelay);
}
else {
response.end();
}
});

server.listen(PORT_NUMBER);
console.log('server listening on port ' + PORT_NUMBER + '...');

if (process.platform === 'win32') {
var readline = require('readline');
var readlineInterface = readline.createInterface({
input: process.stdin,
output: process.stdout
});
readlineInterface.on('SIGINT', function () {
process.emit('SIGINT');
});
}

process.on('SIGINT', function () {
server.close(function () {
console.log('server stopped');
process.exit();
});
});
Loading

0 comments on commit e5d31e8

Please sign in to comment.