зеркало из https://github.com/mozilla/gecko-dev.git
Merge inbound to mozilla-central. a=merge
This commit is contained in:
Коммит
498c659ff8
|
@ -1,5 +1,5 @@
|
|||
This is the PDF.js project output, https://github.com/mozilla/pdf.js
|
||||
|
||||
Current extension version is: 2.1.153
|
||||
Current extension version is: 2.1.176
|
||||
|
||||
Taken from upstream commit: 5a2bd9fc
|
||||
Taken from upstream commit: e4d2a160
|
||||
|
|
|
@ -42,6 +42,7 @@ var PdfJsDefaultPreferences = Object.freeze({
|
|||
"disableOpenActionDestination": true,
|
||||
"disablePageMode": false,
|
||||
"disablePageLabels": false,
|
||||
"historyUpdateUrl": false,
|
||||
"scrollModeOnLoad": 0,
|
||||
"spreadModeOnLoad": 0
|
||||
});
|
||||
|
|
|
@ -123,8 +123,8 @@ return /******/ (function(modules) { // webpackBootstrap
|
|||
"use strict";
|
||||
|
||||
|
||||
var pdfjsVersion = '2.1.153';
|
||||
var pdfjsBuild = '5a2bd9fc';
|
||||
var pdfjsVersion = '2.1.176';
|
||||
var pdfjsBuild = 'e4d2a160';
|
||||
|
||||
var pdfjsSharedUtil = __w_pdfjs_require__(1);
|
||||
|
||||
|
@ -5154,7 +5154,7 @@ function _fetchDocument(worker, source, pdfDataRangeTransport, docId) {
|
|||
|
||||
return worker.messageHandler.sendWithPromise('GetDocRequest', {
|
||||
docId,
|
||||
apiVersion: '2.1.153',
|
||||
apiVersion: '2.1.176',
|
||||
source: {
|
||||
data: source.data,
|
||||
url: source.url,
|
||||
|
@ -6885,9 +6885,9 @@ const InternalRenderTask = function InternalRenderTaskClosure() {
|
|||
return InternalRenderTask;
|
||||
}();
|
||||
|
||||
const version = '2.1.153';
|
||||
const version = '2.1.176';
|
||||
exports.version = version;
|
||||
const build = '5a2bd9fc';
|
||||
const build = 'e4d2a160';
|
||||
exports.build = build;
|
||||
|
||||
/***/ }),
|
||||
|
@ -10699,7 +10699,7 @@ function isWhitespaceString(s) {
|
|||
|
||||
class XMLParserBase {
|
||||
_resolveEntities(s) {
|
||||
return s.replace(/&([^;]+);/g, function (all, entity) {
|
||||
return s.replace(/&([^;]+);/g, (all, entity) => {
|
||||
if (entity.substring(0, 2) === '#x') {
|
||||
return String.fromCharCode(parseInt(entity.substring(2), 16));
|
||||
} else if (entity.substring(0, 1) === '#') {
|
||||
|
@ -10989,6 +10989,11 @@ class SimpleDOMNode {
|
|||
}
|
||||
|
||||
const index = childNodes.indexOf(this);
|
||||
|
||||
if (index === -1) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return childNodes[index + 1];
|
||||
}
|
||||
|
||||
|
@ -11078,9 +11083,13 @@ class SimpleXMLParser extends XMLParserBase {
|
|||
}
|
||||
|
||||
onEndElement(name) {
|
||||
this._currentFragment = this._stack.pop();
|
||||
this._currentFragment = this._stack.pop() || [];
|
||||
const lastElement = this._currentFragment[this._currentFragment.length - 1];
|
||||
|
||||
if (!lastElement) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (let i = 0, ii = lastElement.childNodes.length; i < ii; i++) {
|
||||
lastElement.childNodes[i].parentNode = lastElement;
|
||||
}
|
||||
|
|
|
@ -123,8 +123,8 @@ return /******/ (function(modules) { // webpackBootstrap
|
|||
"use strict";
|
||||
|
||||
|
||||
var pdfjsVersion = '2.1.153';
|
||||
var pdfjsBuild = '5a2bd9fc';
|
||||
var pdfjsVersion = '2.1.176';
|
||||
var pdfjsBuild = 'e4d2a160';
|
||||
|
||||
var pdfjsCoreWorker = __w_pdfjs_require__(1);
|
||||
|
||||
|
@ -375,7 +375,7 @@ var WorkerMessageHandler = {
|
|||
var cancelXHRs = null;
|
||||
var WorkerTasks = [];
|
||||
let apiVersion = docParams.apiVersion;
|
||||
let workerVersion = '2.1.153';
|
||||
let workerVersion = '2.1.176';
|
||||
|
||||
if (apiVersion !== workerVersion) {
|
||||
throw new Error(`The API version "${apiVersion}" does not match ` + `the Worker version "${workerVersion}".`);
|
||||
|
@ -14942,246 +14942,245 @@ Object.defineProperty(exports, "__esModule", {
|
|||
value: true
|
||||
});
|
||||
exports.ArithmeticDecoder = void 0;
|
||||
const QeTable = [{
|
||||
qe: 0x5601,
|
||||
nmps: 1,
|
||||
nlps: 1,
|
||||
switchFlag: 1
|
||||
}, {
|
||||
qe: 0x3401,
|
||||
nmps: 2,
|
||||
nlps: 6,
|
||||
switchFlag: 0
|
||||
}, {
|
||||
qe: 0x1801,
|
||||
nmps: 3,
|
||||
nlps: 9,
|
||||
switchFlag: 0
|
||||
}, {
|
||||
qe: 0x0AC1,
|
||||
nmps: 4,
|
||||
nlps: 12,
|
||||
switchFlag: 0
|
||||
}, {
|
||||
qe: 0x0521,
|
||||
nmps: 5,
|
||||
nlps: 29,
|
||||
switchFlag: 0
|
||||
}, {
|
||||
qe: 0x0221,
|
||||
nmps: 38,
|
||||
nlps: 33,
|
||||
switchFlag: 0
|
||||
}, {
|
||||
qe: 0x5601,
|
||||
nmps: 7,
|
||||
nlps: 6,
|
||||
switchFlag: 1
|
||||
}, {
|
||||
qe: 0x5401,
|
||||
nmps: 8,
|
||||
nlps: 14,
|
||||
switchFlag: 0
|
||||
}, {
|
||||
qe: 0x4801,
|
||||
nmps: 9,
|
||||
nlps: 14,
|
||||
switchFlag: 0
|
||||
}, {
|
||||
qe: 0x3801,
|
||||
nmps: 10,
|
||||
nlps: 14,
|
||||
switchFlag: 0
|
||||
}, {
|
||||
qe: 0x3001,
|
||||
nmps: 11,
|
||||
nlps: 17,
|
||||
switchFlag: 0
|
||||
}, {
|
||||
qe: 0x2401,
|
||||
nmps: 12,
|
||||
nlps: 18,
|
||||
switchFlag: 0
|
||||
}, {
|
||||
qe: 0x1C01,
|
||||
nmps: 13,
|
||||
nlps: 20,
|
||||
switchFlag: 0
|
||||
}, {
|
||||
qe: 0x1601,
|
||||
nmps: 29,
|
||||
nlps: 21,
|
||||
switchFlag: 0
|
||||
}, {
|
||||
qe: 0x5601,
|
||||
nmps: 15,
|
||||
nlps: 14,
|
||||
switchFlag: 1
|
||||
}, {
|
||||
qe: 0x5401,
|
||||
nmps: 16,
|
||||
nlps: 14,
|
||||
switchFlag: 0
|
||||
}, {
|
||||
qe: 0x5101,
|
||||
nmps: 17,
|
||||
nlps: 15,
|
||||
switchFlag: 0
|
||||
}, {
|
||||
qe: 0x4801,
|
||||
nmps: 18,
|
||||
nlps: 16,
|
||||
switchFlag: 0
|
||||
}, {
|
||||
qe: 0x3801,
|
||||
nmps: 19,
|
||||
nlps: 17,
|
||||
switchFlag: 0
|
||||
}, {
|
||||
qe: 0x3401,
|
||||
nmps: 20,
|
||||
nlps: 18,
|
||||
switchFlag: 0
|
||||
}, {
|
||||
qe: 0x3001,
|
||||
nmps: 21,
|
||||
nlps: 19,
|
||||
switchFlag: 0
|
||||
}, {
|
||||
qe: 0x2801,
|
||||
nmps: 22,
|
||||
nlps: 19,
|
||||
switchFlag: 0
|
||||
}, {
|
||||
qe: 0x2401,
|
||||
nmps: 23,
|
||||
nlps: 20,
|
||||
switchFlag: 0
|
||||
}, {
|
||||
qe: 0x2201,
|
||||
nmps: 24,
|
||||
nlps: 21,
|
||||
switchFlag: 0
|
||||
}, {
|
||||
qe: 0x1C01,
|
||||
nmps: 25,
|
||||
nlps: 22,
|
||||
switchFlag: 0
|
||||
}, {
|
||||
qe: 0x1801,
|
||||
nmps: 26,
|
||||
nlps: 23,
|
||||
switchFlag: 0
|
||||
}, {
|
||||
qe: 0x1601,
|
||||
nmps: 27,
|
||||
nlps: 24,
|
||||
switchFlag: 0
|
||||
}, {
|
||||
qe: 0x1401,
|
||||
nmps: 28,
|
||||
nlps: 25,
|
||||
switchFlag: 0
|
||||
}, {
|
||||
qe: 0x1201,
|
||||
nmps: 29,
|
||||
nlps: 26,
|
||||
switchFlag: 0
|
||||
}, {
|
||||
qe: 0x1101,
|
||||
nmps: 30,
|
||||
nlps: 27,
|
||||
switchFlag: 0
|
||||
}, {
|
||||
qe: 0x0AC1,
|
||||
nmps: 31,
|
||||
nlps: 28,
|
||||
switchFlag: 0
|
||||
}, {
|
||||
qe: 0x09C1,
|
||||
nmps: 32,
|
||||
nlps: 29,
|
||||
switchFlag: 0
|
||||
}, {
|
||||
qe: 0x08A1,
|
||||
nmps: 33,
|
||||
nlps: 30,
|
||||
switchFlag: 0
|
||||
}, {
|
||||
qe: 0x0521,
|
||||
nmps: 34,
|
||||
nlps: 31,
|
||||
switchFlag: 0
|
||||
}, {
|
||||
qe: 0x0441,
|
||||
nmps: 35,
|
||||
nlps: 32,
|
||||
switchFlag: 0
|
||||
}, {
|
||||
qe: 0x02A1,
|
||||
nmps: 36,
|
||||
nlps: 33,
|
||||
switchFlag: 0
|
||||
}, {
|
||||
qe: 0x0221,
|
||||
nmps: 37,
|
||||
nlps: 34,
|
||||
switchFlag: 0
|
||||
}, {
|
||||
qe: 0x0141,
|
||||
nmps: 38,
|
||||
nlps: 35,
|
||||
switchFlag: 0
|
||||
}, {
|
||||
qe: 0x0111,
|
||||
nmps: 39,
|
||||
nlps: 36,
|
||||
switchFlag: 0
|
||||
}, {
|
||||
qe: 0x0085,
|
||||
nmps: 40,
|
||||
nlps: 37,
|
||||
switchFlag: 0
|
||||
}, {
|
||||
qe: 0x0049,
|
||||
nmps: 41,
|
||||
nlps: 38,
|
||||
switchFlag: 0
|
||||
}, {
|
||||
qe: 0x0025,
|
||||
nmps: 42,
|
||||
nlps: 39,
|
||||
switchFlag: 0
|
||||
}, {
|
||||
qe: 0x0015,
|
||||
nmps: 43,
|
||||
nlps: 40,
|
||||
switchFlag: 0
|
||||
}, {
|
||||
qe: 0x0009,
|
||||
nmps: 44,
|
||||
nlps: 41,
|
||||
switchFlag: 0
|
||||
}, {
|
||||
qe: 0x0005,
|
||||
nmps: 45,
|
||||
nlps: 42,
|
||||
switchFlag: 0
|
||||
}, {
|
||||
qe: 0x0001,
|
||||
nmps: 45,
|
||||
nlps: 43,
|
||||
switchFlag: 0
|
||||
}, {
|
||||
qe: 0x5601,
|
||||
nmps: 46,
|
||||
nlps: 46,
|
||||
switchFlag: 0
|
||||
}];
|
||||
|
||||
var ArithmeticDecoder = function ArithmeticDecoderClosure() {
|
||||
var QeTable = [{
|
||||
qe: 0x5601,
|
||||
nmps: 1,
|
||||
nlps: 1,
|
||||
switchFlag: 1
|
||||
}, {
|
||||
qe: 0x3401,
|
||||
nmps: 2,
|
||||
nlps: 6,
|
||||
switchFlag: 0
|
||||
}, {
|
||||
qe: 0x1801,
|
||||
nmps: 3,
|
||||
nlps: 9,
|
||||
switchFlag: 0
|
||||
}, {
|
||||
qe: 0x0AC1,
|
||||
nmps: 4,
|
||||
nlps: 12,
|
||||
switchFlag: 0
|
||||
}, {
|
||||
qe: 0x0521,
|
||||
nmps: 5,
|
||||
nlps: 29,
|
||||
switchFlag: 0
|
||||
}, {
|
||||
qe: 0x0221,
|
||||
nmps: 38,
|
||||
nlps: 33,
|
||||
switchFlag: 0
|
||||
}, {
|
||||
qe: 0x5601,
|
||||
nmps: 7,
|
||||
nlps: 6,
|
||||
switchFlag: 1
|
||||
}, {
|
||||
qe: 0x5401,
|
||||
nmps: 8,
|
||||
nlps: 14,
|
||||
switchFlag: 0
|
||||
}, {
|
||||
qe: 0x4801,
|
||||
nmps: 9,
|
||||
nlps: 14,
|
||||
switchFlag: 0
|
||||
}, {
|
||||
qe: 0x3801,
|
||||
nmps: 10,
|
||||
nlps: 14,
|
||||
switchFlag: 0
|
||||
}, {
|
||||
qe: 0x3001,
|
||||
nmps: 11,
|
||||
nlps: 17,
|
||||
switchFlag: 0
|
||||
}, {
|
||||
qe: 0x2401,
|
||||
nmps: 12,
|
||||
nlps: 18,
|
||||
switchFlag: 0
|
||||
}, {
|
||||
qe: 0x1C01,
|
||||
nmps: 13,
|
||||
nlps: 20,
|
||||
switchFlag: 0
|
||||
}, {
|
||||
qe: 0x1601,
|
||||
nmps: 29,
|
||||
nlps: 21,
|
||||
switchFlag: 0
|
||||
}, {
|
||||
qe: 0x5601,
|
||||
nmps: 15,
|
||||
nlps: 14,
|
||||
switchFlag: 1
|
||||
}, {
|
||||
qe: 0x5401,
|
||||
nmps: 16,
|
||||
nlps: 14,
|
||||
switchFlag: 0
|
||||
}, {
|
||||
qe: 0x5101,
|
||||
nmps: 17,
|
||||
nlps: 15,
|
||||
switchFlag: 0
|
||||
}, {
|
||||
qe: 0x4801,
|
||||
nmps: 18,
|
||||
nlps: 16,
|
||||
switchFlag: 0
|
||||
}, {
|
||||
qe: 0x3801,
|
||||
nmps: 19,
|
||||
nlps: 17,
|
||||
switchFlag: 0
|
||||
}, {
|
||||
qe: 0x3401,
|
||||
nmps: 20,
|
||||
nlps: 18,
|
||||
switchFlag: 0
|
||||
}, {
|
||||
qe: 0x3001,
|
||||
nmps: 21,
|
||||
nlps: 19,
|
||||
switchFlag: 0
|
||||
}, {
|
||||
qe: 0x2801,
|
||||
nmps: 22,
|
||||
nlps: 19,
|
||||
switchFlag: 0
|
||||
}, {
|
||||
qe: 0x2401,
|
||||
nmps: 23,
|
||||
nlps: 20,
|
||||
switchFlag: 0
|
||||
}, {
|
||||
qe: 0x2201,
|
||||
nmps: 24,
|
||||
nlps: 21,
|
||||
switchFlag: 0
|
||||
}, {
|
||||
qe: 0x1C01,
|
||||
nmps: 25,
|
||||
nlps: 22,
|
||||
switchFlag: 0
|
||||
}, {
|
||||
qe: 0x1801,
|
||||
nmps: 26,
|
||||
nlps: 23,
|
||||
switchFlag: 0
|
||||
}, {
|
||||
qe: 0x1601,
|
||||
nmps: 27,
|
||||
nlps: 24,
|
||||
switchFlag: 0
|
||||
}, {
|
||||
qe: 0x1401,
|
||||
nmps: 28,
|
||||
nlps: 25,
|
||||
switchFlag: 0
|
||||
}, {
|
||||
qe: 0x1201,
|
||||
nmps: 29,
|
||||
nlps: 26,
|
||||
switchFlag: 0
|
||||
}, {
|
||||
qe: 0x1101,
|
||||
nmps: 30,
|
||||
nlps: 27,
|
||||
switchFlag: 0
|
||||
}, {
|
||||
qe: 0x0AC1,
|
||||
nmps: 31,
|
||||
nlps: 28,
|
||||
switchFlag: 0
|
||||
}, {
|
||||
qe: 0x09C1,
|
||||
nmps: 32,
|
||||
nlps: 29,
|
||||
switchFlag: 0
|
||||
}, {
|
||||
qe: 0x08A1,
|
||||
nmps: 33,
|
||||
nlps: 30,
|
||||
switchFlag: 0
|
||||
}, {
|
||||
qe: 0x0521,
|
||||
nmps: 34,
|
||||
nlps: 31,
|
||||
switchFlag: 0
|
||||
}, {
|
||||
qe: 0x0441,
|
||||
nmps: 35,
|
||||
nlps: 32,
|
||||
switchFlag: 0
|
||||
}, {
|
||||
qe: 0x02A1,
|
||||
nmps: 36,
|
||||
nlps: 33,
|
||||
switchFlag: 0
|
||||
}, {
|
||||
qe: 0x0221,
|
||||
nmps: 37,
|
||||
nlps: 34,
|
||||
switchFlag: 0
|
||||
}, {
|
||||
qe: 0x0141,
|
||||
nmps: 38,
|
||||
nlps: 35,
|
||||
switchFlag: 0
|
||||
}, {
|
||||
qe: 0x0111,
|
||||
nmps: 39,
|
||||
nlps: 36,
|
||||
switchFlag: 0
|
||||
}, {
|
||||
qe: 0x0085,
|
||||
nmps: 40,
|
||||
nlps: 37,
|
||||
switchFlag: 0
|
||||
}, {
|
||||
qe: 0x0049,
|
||||
nmps: 41,
|
||||
nlps: 38,
|
||||
switchFlag: 0
|
||||
}, {
|
||||
qe: 0x0025,
|
||||
nmps: 42,
|
||||
nlps: 39,
|
||||
switchFlag: 0
|
||||
}, {
|
||||
qe: 0x0015,
|
||||
nmps: 43,
|
||||
nlps: 40,
|
||||
switchFlag: 0
|
||||
}, {
|
||||
qe: 0x0009,
|
||||
nmps: 44,
|
||||
nlps: 41,
|
||||
switchFlag: 0
|
||||
}, {
|
||||
qe: 0x0005,
|
||||
nmps: 45,
|
||||
nlps: 42,
|
||||
switchFlag: 0
|
||||
}, {
|
||||
qe: 0x0001,
|
||||
nmps: 45,
|
||||
nlps: 43,
|
||||
switchFlag: 0
|
||||
}, {
|
||||
qe: 0x5601,
|
||||
nmps: 46,
|
||||
nlps: 46,
|
||||
switchFlag: 0
|
||||
}];
|
||||
|
||||
function ArithmeticDecoder(data, start, end) {
|
||||
class ArithmeticDecoder {
|
||||
constructor(data, start, end) {
|
||||
this.data = data;
|
||||
this.bp = start;
|
||||
this.dataEnd = end;
|
||||
|
@ -15194,98 +15193,95 @@ var ArithmeticDecoder = function ArithmeticDecoderClosure() {
|
|||
this.a = 0x8000;
|
||||
}
|
||||
|
||||
ArithmeticDecoder.prototype = {
|
||||
byteIn: function ArithmeticDecoder_byteIn() {
|
||||
var data = this.data;
|
||||
var bp = this.bp;
|
||||
byteIn() {
|
||||
const data = this.data;
|
||||
let bp = this.bp;
|
||||
|
||||
if (data[bp] === 0xFF) {
|
||||
var b1 = data[bp + 1];
|
||||
|
||||
if (b1 > 0x8F) {
|
||||
this.clow += 0xFF00;
|
||||
this.ct = 8;
|
||||
} else {
|
||||
bp++;
|
||||
this.clow += data[bp] << 9;
|
||||
this.ct = 7;
|
||||
this.bp = bp;
|
||||
}
|
||||
if (data[bp] === 0xFF) {
|
||||
if (data[bp + 1] > 0x8F) {
|
||||
this.clow += 0xFF00;
|
||||
this.ct = 8;
|
||||
} else {
|
||||
bp++;
|
||||
this.clow += bp < this.dataEnd ? data[bp] << 8 : 0xFF00;
|
||||
this.ct = 8;
|
||||
this.clow += data[bp] << 9;
|
||||
this.ct = 7;
|
||||
this.bp = bp;
|
||||
}
|
||||
|
||||
if (this.clow > 0xFFFF) {
|
||||
this.chigh += this.clow >> 16;
|
||||
this.clow &= 0xFFFF;
|
||||
}
|
||||
},
|
||||
readBit: function ArithmeticDecoder_readBit(contexts, pos) {
|
||||
var cx_index = contexts[pos] >> 1,
|
||||
cx_mps = contexts[pos] & 1;
|
||||
var qeTableIcx = QeTable[cx_index];
|
||||
var qeIcx = qeTableIcx.qe;
|
||||
var d;
|
||||
var a = this.a - qeIcx;
|
||||
|
||||
if (this.chigh < qeIcx) {
|
||||
if (a < qeIcx) {
|
||||
a = qeIcx;
|
||||
d = cx_mps;
|
||||
cx_index = qeTableIcx.nmps;
|
||||
} else {
|
||||
a = qeIcx;
|
||||
d = 1 ^ cx_mps;
|
||||
|
||||
if (qeTableIcx.switchFlag === 1) {
|
||||
cx_mps = d;
|
||||
}
|
||||
|
||||
cx_index = qeTableIcx.nlps;
|
||||
}
|
||||
} else {
|
||||
this.chigh -= qeIcx;
|
||||
|
||||
if ((a & 0x8000) !== 0) {
|
||||
this.a = a;
|
||||
return cx_mps;
|
||||
}
|
||||
|
||||
if (a < qeIcx) {
|
||||
d = 1 ^ cx_mps;
|
||||
|
||||
if (qeTableIcx.switchFlag === 1) {
|
||||
cx_mps = d;
|
||||
}
|
||||
|
||||
cx_index = qeTableIcx.nlps;
|
||||
} else {
|
||||
d = cx_mps;
|
||||
cx_index = qeTableIcx.nmps;
|
||||
}
|
||||
}
|
||||
|
||||
do {
|
||||
if (this.ct === 0) {
|
||||
this.byteIn();
|
||||
}
|
||||
|
||||
a <<= 1;
|
||||
this.chigh = this.chigh << 1 & 0xFFFF | this.clow >> 15 & 1;
|
||||
this.clow = this.clow << 1 & 0xFFFF;
|
||||
this.ct--;
|
||||
} while ((a & 0x8000) === 0);
|
||||
|
||||
this.a = a;
|
||||
contexts[pos] = cx_index << 1 | cx_mps;
|
||||
return d;
|
||||
} else {
|
||||
bp++;
|
||||
this.clow += bp < this.dataEnd ? data[bp] << 8 : 0xFF00;
|
||||
this.ct = 8;
|
||||
this.bp = bp;
|
||||
}
|
||||
};
|
||||
return ArithmeticDecoder;
|
||||
}();
|
||||
|
||||
if (this.clow > 0xFFFF) {
|
||||
this.chigh += this.clow >> 16;
|
||||
this.clow &= 0xFFFF;
|
||||
}
|
||||
}
|
||||
|
||||
readBit(contexts, pos) {
|
||||
let cx_index = contexts[pos] >> 1,
|
||||
cx_mps = contexts[pos] & 1;
|
||||
const qeTableIcx = QeTable[cx_index];
|
||||
const qeIcx = qeTableIcx.qe;
|
||||
let d;
|
||||
let a = this.a - qeIcx;
|
||||
|
||||
if (this.chigh < qeIcx) {
|
||||
if (a < qeIcx) {
|
||||
a = qeIcx;
|
||||
d = cx_mps;
|
||||
cx_index = qeTableIcx.nmps;
|
||||
} else {
|
||||
a = qeIcx;
|
||||
d = 1 ^ cx_mps;
|
||||
|
||||
if (qeTableIcx.switchFlag === 1) {
|
||||
cx_mps = d;
|
||||
}
|
||||
|
||||
cx_index = qeTableIcx.nlps;
|
||||
}
|
||||
} else {
|
||||
this.chigh -= qeIcx;
|
||||
|
||||
if ((a & 0x8000) !== 0) {
|
||||
this.a = a;
|
||||
return cx_mps;
|
||||
}
|
||||
|
||||
if (a < qeIcx) {
|
||||
d = 1 ^ cx_mps;
|
||||
|
||||
if (qeTableIcx.switchFlag === 1) {
|
||||
cx_mps = d;
|
||||
}
|
||||
|
||||
cx_index = qeTableIcx.nlps;
|
||||
} else {
|
||||
d = cx_mps;
|
||||
cx_index = qeTableIcx.nmps;
|
||||
}
|
||||
}
|
||||
|
||||
do {
|
||||
if (this.ct === 0) {
|
||||
this.byteIn();
|
||||
}
|
||||
|
||||
a <<= 1;
|
||||
this.chigh = this.chigh << 1 & 0xFFFF | this.clow >> 15 & 1;
|
||||
this.clow = this.clow << 1 & 0xFFFF;
|
||||
this.ct--;
|
||||
} while ((a & 0x8000) === 0);
|
||||
|
||||
this.a = a;
|
||||
contexts[pos] = cx_index << 1 | cx_mps;
|
||||
return d;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
exports.ArithmeticDecoder = ArithmeticDecoder;
|
||||
|
||||
|
@ -21985,7 +21981,8 @@ class AnnotationBorderStyle {
|
|||
|
||||
setWidth(width) {
|
||||
if ((0, _primitives.isName)(width)) {
|
||||
width = parseFloat(width.name);
|
||||
this.width = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
if (Number.isInteger(width)) {
|
||||
|
@ -28769,7 +28766,19 @@ var Font = function FontClosure() {
|
|||
}
|
||||
|
||||
font.pos = (font.start ? font.start : 0) + header.offset;
|
||||
font.pos += header.length - 2;
|
||||
font.pos += 4;
|
||||
font.pos += 2;
|
||||
font.pos += 2;
|
||||
font.pos += 2;
|
||||
font.pos += 2;
|
||||
font.pos += 2;
|
||||
font.pos += 2;
|
||||
font.pos += 2;
|
||||
font.pos += 2;
|
||||
font.pos += 2;
|
||||
font.pos += 2;
|
||||
font.pos += 8;
|
||||
font.pos += 2;
|
||||
var numOfMetrics = font.getUint16();
|
||||
|
||||
if (numOfMetrics > numGlyphs) {
|
||||
|
@ -32260,11 +32269,11 @@ Object.defineProperty(exports, "__esModule", {
|
|||
value: true
|
||||
});
|
||||
exports.ExpertSubsetCharset = exports.ExpertCharset = exports.ISOAdobeCharset = void 0;
|
||||
var ISOAdobeCharset = ['.notdef', 'space', 'exclam', 'quotedbl', 'numbersign', 'dollar', 'percent', 'ampersand', 'quoteright', 'parenleft', 'parenright', 'asterisk', 'plus', 'comma', 'hyphen', 'period', 'slash', 'zero', 'one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine', 'colon', 'semicolon', 'less', 'equal', 'greater', 'question', 'at', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'bracketleft', 'backslash', 'bracketright', 'asciicircum', 'underscore', 'quoteleft', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'braceleft', 'bar', 'braceright', 'asciitilde', 'exclamdown', 'cent', 'sterling', 'fraction', 'yen', 'florin', 'section', 'currency', 'quotesingle', 'quotedblleft', 'guillemotleft', 'guilsinglleft', 'guilsinglright', 'fi', 'fl', 'endash', 'dagger', 'daggerdbl', 'periodcentered', 'paragraph', 'bullet', 'quotesinglbase', 'quotedblbase', 'quotedblright', 'guillemotright', 'ellipsis', 'perthousand', 'questiondown', 'grave', 'acute', 'circumflex', 'tilde', 'macron', 'breve', 'dotaccent', 'dieresis', 'ring', 'cedilla', 'hungarumlaut', 'ogonek', 'caron', 'emdash', 'AE', 'ordfeminine', 'Lslash', 'Oslash', 'OE', 'ordmasculine', 'ae', 'dotlessi', 'lslash', 'oslash', 'oe', 'germandbls', 'onesuperior', 'logicalnot', 'mu', 'trademark', 'Eth', 'onehalf', 'plusminus', 'Thorn', 'onequarter', 'divide', 'brokenbar', 'degree', 'thorn', 'threequarters', 'twosuperior', 'registered', 'minus', 'eth', 'multiply', 'threesuperior', 'copyright', 'Aacute', 'Acircumflex', 'Adieresis', 'Agrave', 'Aring', 'Atilde', 'Ccedilla', 'Eacute', 'Ecircumflex', 'Edieresis', 'Egrave', 'Iacute', 'Icircumflex', 'Idieresis', 'Igrave', 'Ntilde', 'Oacute', 'Ocircumflex', 'Odieresis', 'Ograve', 'Otilde', 'Scaron', 'Uacute', 'Ucircumflex', 'Udieresis', 'Ugrave', 'Yacute', 'Ydieresis', 'Zcaron', 'aacute', 'acircumflex', 'adieresis', 'agrave', 'aring', 'atilde', 'ccedilla', 'eacute', 'ecircumflex', 'edieresis', 'egrave', 'iacute', 'icircumflex', 'idieresis', 'igrave', 'ntilde', 'oacute', 'ocircumflex', 'odieresis', 'ograve', 'otilde', 'scaron', 'uacute', 'ucircumflex', 'udieresis', 'ugrave', 'yacute', 'ydieresis', 'zcaron'];
|
||||
const ISOAdobeCharset = ['.notdef', 'space', 'exclam', 'quotedbl', 'numbersign', 'dollar', 'percent', 'ampersand', 'quoteright', 'parenleft', 'parenright', 'asterisk', 'plus', 'comma', 'hyphen', 'period', 'slash', 'zero', 'one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine', 'colon', 'semicolon', 'less', 'equal', 'greater', 'question', 'at', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'bracketleft', 'backslash', 'bracketright', 'asciicircum', 'underscore', 'quoteleft', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'braceleft', 'bar', 'braceright', 'asciitilde', 'exclamdown', 'cent', 'sterling', 'fraction', 'yen', 'florin', 'section', 'currency', 'quotesingle', 'quotedblleft', 'guillemotleft', 'guilsinglleft', 'guilsinglright', 'fi', 'fl', 'endash', 'dagger', 'daggerdbl', 'periodcentered', 'paragraph', 'bullet', 'quotesinglbase', 'quotedblbase', 'quotedblright', 'guillemotright', 'ellipsis', 'perthousand', 'questiondown', 'grave', 'acute', 'circumflex', 'tilde', 'macron', 'breve', 'dotaccent', 'dieresis', 'ring', 'cedilla', 'hungarumlaut', 'ogonek', 'caron', 'emdash', 'AE', 'ordfeminine', 'Lslash', 'Oslash', 'OE', 'ordmasculine', 'ae', 'dotlessi', 'lslash', 'oslash', 'oe', 'germandbls', 'onesuperior', 'logicalnot', 'mu', 'trademark', 'Eth', 'onehalf', 'plusminus', 'Thorn', 'onequarter', 'divide', 'brokenbar', 'degree', 'thorn', 'threequarters', 'twosuperior', 'registered', 'minus', 'eth', 'multiply', 'threesuperior', 'copyright', 'Aacute', 'Acircumflex', 'Adieresis', 'Agrave', 'Aring', 'Atilde', 'Ccedilla', 'Eacute', 'Ecircumflex', 'Edieresis', 'Egrave', 'Iacute', 'Icircumflex', 'Idieresis', 'Igrave', 'Ntilde', 'Oacute', 'Ocircumflex', 'Odieresis', 'Ograve', 'Otilde', 'Scaron', 'Uacute', 'Ucircumflex', 'Udieresis', 'Ugrave', 'Yacute', 'Ydieresis', 'Zcaron', 'aacute', 'acircumflex', 'adieresis', 'agrave', 'aring', 'atilde', 'ccedilla', 'eacute', 'ecircumflex', 'edieresis', 'egrave', 'iacute', 'icircumflex', 'idieresis', 'igrave', 'ntilde', 'oacute', 'ocircumflex', 'odieresis', 'ograve', 'otilde', 'scaron', 'uacute', 'ucircumflex', 'udieresis', 'ugrave', 'yacute', 'ydieresis', 'zcaron'];
|
||||
exports.ISOAdobeCharset = ISOAdobeCharset;
|
||||
var ExpertCharset = ['.notdef', 'space', 'exclamsmall', 'Hungarumlautsmall', 'dollaroldstyle', 'dollarsuperior', 'ampersandsmall', 'Acutesmall', 'parenleftsuperior', 'parenrightsuperior', 'twodotenleader', 'onedotenleader', 'comma', 'hyphen', 'period', 'fraction', 'zerooldstyle', 'oneoldstyle', 'twooldstyle', 'threeoldstyle', 'fouroldstyle', 'fiveoldstyle', 'sixoldstyle', 'sevenoldstyle', 'eightoldstyle', 'nineoldstyle', 'colon', 'semicolon', 'commasuperior', 'threequartersemdash', 'periodsuperior', 'questionsmall', 'asuperior', 'bsuperior', 'centsuperior', 'dsuperior', 'esuperior', 'isuperior', 'lsuperior', 'msuperior', 'nsuperior', 'osuperior', 'rsuperior', 'ssuperior', 'tsuperior', 'ff', 'fi', 'fl', 'ffi', 'ffl', 'parenleftinferior', 'parenrightinferior', 'Circumflexsmall', 'hyphensuperior', 'Gravesmall', 'Asmall', 'Bsmall', 'Csmall', 'Dsmall', 'Esmall', 'Fsmall', 'Gsmall', 'Hsmall', 'Ismall', 'Jsmall', 'Ksmall', 'Lsmall', 'Msmall', 'Nsmall', 'Osmall', 'Psmall', 'Qsmall', 'Rsmall', 'Ssmall', 'Tsmall', 'Usmall', 'Vsmall', 'Wsmall', 'Xsmall', 'Ysmall', 'Zsmall', 'colonmonetary', 'onefitted', 'rupiah', 'Tildesmall', 'exclamdownsmall', 'centoldstyle', 'Lslashsmall', 'Scaronsmall', 'Zcaronsmall', 'Dieresissmall', 'Brevesmall', 'Caronsmall', 'Dotaccentsmall', 'Macronsmall', 'figuredash', 'hypheninferior', 'Ogoneksmall', 'Ringsmall', 'Cedillasmall', 'onequarter', 'onehalf', 'threequarters', 'questiondownsmall', 'oneeighth', 'threeeighths', 'fiveeighths', 'seveneighths', 'onethird', 'twothirds', 'zerosuperior', 'onesuperior', 'twosuperior', 'threesuperior', 'foursuperior', 'fivesuperior', 'sixsuperior', 'sevensuperior', 'eightsuperior', 'ninesuperior', 'zeroinferior', 'oneinferior', 'twoinferior', 'threeinferior', 'fourinferior', 'fiveinferior', 'sixinferior', 'seveninferior', 'eightinferior', 'nineinferior', 'centinferior', 'dollarinferior', 'periodinferior', 'commainferior', 'Agravesmall', 'Aacutesmall', 'Acircumflexsmall', 'Atildesmall', 'Adieresissmall', 'Aringsmall', 'AEsmall', 'Ccedillasmall', 'Egravesmall', 'Eacutesmall', 'Ecircumflexsmall', 'Edieresissmall', 'Igravesmall', 'Iacutesmall', 'Icircumflexsmall', 'Idieresissmall', 'Ethsmall', 'Ntildesmall', 'Ogravesmall', 'Oacutesmall', 'Ocircumflexsmall', 'Otildesmall', 'Odieresissmall', 'OEsmall', 'Oslashsmall', 'Ugravesmall', 'Uacutesmall', 'Ucircumflexsmall', 'Udieresissmall', 'Yacutesmall', 'Thornsmall', 'Ydieresissmall'];
|
||||
const ExpertCharset = ['.notdef', 'space', 'exclamsmall', 'Hungarumlautsmall', 'dollaroldstyle', 'dollarsuperior', 'ampersandsmall', 'Acutesmall', 'parenleftsuperior', 'parenrightsuperior', 'twodotenleader', 'onedotenleader', 'comma', 'hyphen', 'period', 'fraction', 'zerooldstyle', 'oneoldstyle', 'twooldstyle', 'threeoldstyle', 'fouroldstyle', 'fiveoldstyle', 'sixoldstyle', 'sevenoldstyle', 'eightoldstyle', 'nineoldstyle', 'colon', 'semicolon', 'commasuperior', 'threequartersemdash', 'periodsuperior', 'questionsmall', 'asuperior', 'bsuperior', 'centsuperior', 'dsuperior', 'esuperior', 'isuperior', 'lsuperior', 'msuperior', 'nsuperior', 'osuperior', 'rsuperior', 'ssuperior', 'tsuperior', 'ff', 'fi', 'fl', 'ffi', 'ffl', 'parenleftinferior', 'parenrightinferior', 'Circumflexsmall', 'hyphensuperior', 'Gravesmall', 'Asmall', 'Bsmall', 'Csmall', 'Dsmall', 'Esmall', 'Fsmall', 'Gsmall', 'Hsmall', 'Ismall', 'Jsmall', 'Ksmall', 'Lsmall', 'Msmall', 'Nsmall', 'Osmall', 'Psmall', 'Qsmall', 'Rsmall', 'Ssmall', 'Tsmall', 'Usmall', 'Vsmall', 'Wsmall', 'Xsmall', 'Ysmall', 'Zsmall', 'colonmonetary', 'onefitted', 'rupiah', 'Tildesmall', 'exclamdownsmall', 'centoldstyle', 'Lslashsmall', 'Scaronsmall', 'Zcaronsmall', 'Dieresissmall', 'Brevesmall', 'Caronsmall', 'Dotaccentsmall', 'Macronsmall', 'figuredash', 'hypheninferior', 'Ogoneksmall', 'Ringsmall', 'Cedillasmall', 'onequarter', 'onehalf', 'threequarters', 'questiondownsmall', 'oneeighth', 'threeeighths', 'fiveeighths', 'seveneighths', 'onethird', 'twothirds', 'zerosuperior', 'onesuperior', 'twosuperior', 'threesuperior', 'foursuperior', 'fivesuperior', 'sixsuperior', 'sevensuperior', 'eightsuperior', 'ninesuperior', 'zeroinferior', 'oneinferior', 'twoinferior', 'threeinferior', 'fourinferior', 'fiveinferior', 'sixinferior', 'seveninferior', 'eightinferior', 'nineinferior', 'centinferior', 'dollarinferior', 'periodinferior', 'commainferior', 'Agravesmall', 'Aacutesmall', 'Acircumflexsmall', 'Atildesmall', 'Adieresissmall', 'Aringsmall', 'AEsmall', 'Ccedillasmall', 'Egravesmall', 'Eacutesmall', 'Ecircumflexsmall', 'Edieresissmall', 'Igravesmall', 'Iacutesmall', 'Icircumflexsmall', 'Idieresissmall', 'Ethsmall', 'Ntildesmall', 'Ogravesmall', 'Oacutesmall', 'Ocircumflexsmall', 'Otildesmall', 'Odieresissmall', 'OEsmall', 'Oslashsmall', 'Ugravesmall', 'Uacutesmall', 'Ucircumflexsmall', 'Udieresissmall', 'Yacutesmall', 'Thornsmall', 'Ydieresissmall'];
|
||||
exports.ExpertCharset = ExpertCharset;
|
||||
var ExpertSubsetCharset = ['.notdef', 'space', 'dollaroldstyle', 'dollarsuperior', 'parenleftsuperior', 'parenrightsuperior', 'twodotenleader', 'onedotenleader', 'comma', 'hyphen', 'period', 'fraction', 'zerooldstyle', 'oneoldstyle', 'twooldstyle', 'threeoldstyle', 'fouroldstyle', 'fiveoldstyle', 'sixoldstyle', 'sevenoldstyle', 'eightoldstyle', 'nineoldstyle', 'colon', 'semicolon', 'commasuperior', 'threequartersemdash', 'periodsuperior', 'asuperior', 'bsuperior', 'centsuperior', 'dsuperior', 'esuperior', 'isuperior', 'lsuperior', 'msuperior', 'nsuperior', 'osuperior', 'rsuperior', 'ssuperior', 'tsuperior', 'ff', 'fi', 'fl', 'ffi', 'ffl', 'parenleftinferior', 'parenrightinferior', 'hyphensuperior', 'colonmonetary', 'onefitted', 'rupiah', 'centoldstyle', 'figuredash', 'hypheninferior', 'onequarter', 'onehalf', 'threequarters', 'oneeighth', 'threeeighths', 'fiveeighths', 'seveneighths', 'onethird', 'twothirds', 'zerosuperior', 'onesuperior', 'twosuperior', 'threesuperior', 'foursuperior', 'fivesuperior', 'sixsuperior', 'sevensuperior', 'eightsuperior', 'ninesuperior', 'zeroinferior', 'oneinferior', 'twoinferior', 'threeinferior', 'fourinferior', 'fiveinferior', 'sixinferior', 'seveninferior', 'eightinferior', 'nineinferior', 'centinferior', 'dollarinferior', 'periodinferior', 'commainferior'];
|
||||
const ExpertSubsetCharset = ['.notdef', 'space', 'dollaroldstyle', 'dollarsuperior', 'parenleftsuperior', 'parenrightsuperior', 'twodotenleader', 'onedotenleader', 'comma', 'hyphen', 'period', 'fraction', 'zerooldstyle', 'oneoldstyle', 'twooldstyle', 'threeoldstyle', 'fouroldstyle', 'fiveoldstyle', 'sixoldstyle', 'sevenoldstyle', 'eightoldstyle', 'nineoldstyle', 'colon', 'semicolon', 'commasuperior', 'threequartersemdash', 'periodsuperior', 'asuperior', 'bsuperior', 'centsuperior', 'dsuperior', 'esuperior', 'isuperior', 'lsuperior', 'msuperior', 'nsuperior', 'osuperior', 'rsuperior', 'ssuperior', 'tsuperior', 'ff', 'fi', 'fl', 'ffi', 'ffl', 'parenleftinferior', 'parenrightinferior', 'hyphensuperior', 'colonmonetary', 'onefitted', 'rupiah', 'centoldstyle', 'figuredash', 'hypheninferior', 'onequarter', 'onehalf', 'threequarters', 'oneeighth', 'threeeighths', 'fiveeighths', 'seveneighths', 'onethird', 'twothirds', 'zerosuperior', 'onesuperior', 'twosuperior', 'threesuperior', 'foursuperior', 'fivesuperior', 'sixsuperior', 'sevensuperior', 'eightsuperior', 'ninesuperior', 'zeroinferior', 'oneinferior', 'twoinferior', 'threeinferior', 'fourinferior', 'fiveinferior', 'sixinferior', 'seveninferior', 'eightinferior', 'nineinferior', 'centinferior', 'dollarinferior', 'periodinferior', 'commainferior'];
|
||||
exports.ExpertSubsetCharset = ExpertSubsetCharset;
|
||||
|
||||
/***/ }),
|
||||
|
@ -36871,7 +36880,7 @@ exports.getSupplementalGlyphMapForCalibri = exports.getSupplementalGlyphMapForAr
|
|||
|
||||
var _util = __w_pdfjs_require__(2);
|
||||
|
||||
var getStdFontMap = (0, _util.getLookupTableFactory)(function (t) {
|
||||
const getStdFontMap = (0, _util.getLookupTableFactory)(function (t) {
|
||||
t['ArialNarrow'] = 'Helvetica';
|
||||
t['ArialNarrow-Bold'] = 'Helvetica-Bold';
|
||||
t['ArialNarrow-BoldItalic'] = 'Helvetica-BoldOblique';
|
||||
|
@ -36930,7 +36939,7 @@ var getStdFontMap = (0, _util.getLookupTableFactory)(function (t) {
|
|||
t['TimesNewRomanPSMT-Italic'] = 'Times-Italic';
|
||||
});
|
||||
exports.getStdFontMap = getStdFontMap;
|
||||
var getNonStdFontMap = (0, _util.getLookupTableFactory)(function (t) {
|
||||
const getNonStdFontMap = (0, _util.getLookupTableFactory)(function (t) {
|
||||
t['Calibri'] = 'Helvetica';
|
||||
t['Calibri-Bold'] = 'Helvetica-Bold';
|
||||
t['Calibri-BoldItalic'] = 'Helvetica-BoldOblique';
|
||||
|
@ -36968,7 +36977,7 @@ var getNonStdFontMap = (0, _util.getLookupTableFactory)(function (t) {
|
|||
t['Wingdings'] = 'ZapfDingbats';
|
||||
});
|
||||
exports.getNonStdFontMap = getNonStdFontMap;
|
||||
var getSerifFonts = (0, _util.getLookupTableFactory)(function (t) {
|
||||
const getSerifFonts = (0, _util.getLookupTableFactory)(function (t) {
|
||||
t['Adobe Jenson'] = true;
|
||||
t['Adobe Text'] = true;
|
||||
t['Albertus'] = true;
|
||||
|
@ -37104,13 +37113,13 @@ var getSerifFonts = (0, _util.getLookupTableFactory)(function (t) {
|
|||
t['XITS'] = true;
|
||||
});
|
||||
exports.getSerifFonts = getSerifFonts;
|
||||
var getSymbolsFonts = (0, _util.getLookupTableFactory)(function (t) {
|
||||
const getSymbolsFonts = (0, _util.getLookupTableFactory)(function (t) {
|
||||
t['Dingbats'] = true;
|
||||
t['Symbol'] = true;
|
||||
t['ZapfDingbats'] = true;
|
||||
});
|
||||
exports.getSymbolsFonts = getSymbolsFonts;
|
||||
var getGlyphMapForStandardFonts = (0, _util.getLookupTableFactory)(function (t) {
|
||||
const getGlyphMapForStandardFonts = (0, _util.getLookupTableFactory)(function (t) {
|
||||
t[2] = 10;
|
||||
t[3] = 32;
|
||||
t[4] = 33;
|
||||
|
@ -37506,13 +37515,13 @@ var getGlyphMapForStandardFonts = (0, _util.getLookupTableFactory)(function (t)
|
|||
t[3416] = 8377;
|
||||
});
|
||||
exports.getGlyphMapForStandardFonts = getGlyphMapForStandardFonts;
|
||||
var getSupplementalGlyphMapForArialBlack = (0, _util.getLookupTableFactory)(function (t) {
|
||||
const getSupplementalGlyphMapForArialBlack = (0, _util.getLookupTableFactory)(function (t) {
|
||||
t[227] = 322;
|
||||
t[264] = 261;
|
||||
t[291] = 346;
|
||||
});
|
||||
exports.getSupplementalGlyphMapForArialBlack = getSupplementalGlyphMapForArialBlack;
|
||||
let getSupplementalGlyphMapForCalibri = (0, _util.getLookupTableFactory)(function (t) {
|
||||
const getSupplementalGlyphMapForCalibri = (0, _util.getLookupTableFactory)(function (t) {
|
||||
t[1] = 32;
|
||||
t[4] = 65;
|
||||
t[17] = 66;
|
||||
|
|
|
@ -1021,8 +1021,11 @@ let PDFViewerApplication = {
|
|||
this.loadingBar.setWidth(this.appConfig.viewerContainer);
|
||||
|
||||
if (!_app_options.AppOptions.get('disableHistory') && !this.isViewerEmbedded) {
|
||||
let resetHistory = !_app_options.AppOptions.get('showPreviousViewOnLoad');
|
||||
this.pdfHistory.initialize(pdfDocument.fingerprint, resetHistory);
|
||||
this.pdfHistory.initialize({
|
||||
fingerprint: pdfDocument.fingerprint,
|
||||
resetHistory: !_app_options.AppOptions.get('showPreviousViewOnLoad'),
|
||||
updateUrl: _app_options.AppOptions.get('historyUpdateUrl')
|
||||
});
|
||||
|
||||
if (this.pdfHistory.initialBookmark) {
|
||||
this.initialBookmark = this.pdfHistory.initialBookmark;
|
||||
|
@ -3928,6 +3931,10 @@ const defaultOptions = {
|
|||
value: 0,
|
||||
kind: OptionKind.VIEWER
|
||||
},
|
||||
historyUpdateUrl: {
|
||||
value: false,
|
||||
kind: OptionKind.VIEWER
|
||||
},
|
||||
imageResourcesPath: {
|
||||
value: './images/',
|
||||
kind: OptionKind.VIEWER
|
||||
|
@ -5798,7 +5805,11 @@ class PDFHistory {
|
|||
});
|
||||
}
|
||||
|
||||
initialize(fingerprint, resetHistory = false) {
|
||||
initialize({
|
||||
fingerprint,
|
||||
resetHistory = false,
|
||||
updateUrl = false
|
||||
}) {
|
||||
if (!fingerprint || typeof fingerprint !== 'string') {
|
||||
console.error('PDFHistory.initialize: The "fingerprint" must be a non-empty string.');
|
||||
return;
|
||||
|
@ -5806,6 +5817,7 @@ class PDFHistory {
|
|||
|
||||
let reInitialized = this.initialized && this.fingerprint !== fingerprint;
|
||||
this.fingerprint = fingerprint;
|
||||
this._updateUrl = updateUrl === true;
|
||||
|
||||
if (!this.initialized) {
|
||||
this._bindEvents();
|
||||
|
@ -5823,7 +5835,7 @@ class PDFHistory {
|
|||
this._destination = null;
|
||||
this._position = null;
|
||||
|
||||
if (!this._isValidState(state) || resetHistory) {
|
||||
if (!this._isValidState(state, true) || resetHistory) {
|
||||
let {
|
||||
hash,
|
||||
page,
|
||||
|
@ -5970,11 +5982,21 @@ class PDFHistory {
|
|||
|
||||
this._updateInternalState(destination, newState.uid);
|
||||
|
||||
let newUrl;
|
||||
|
||||
if (this._updateUrl && destination && destination.hash) {
|
||||
const baseUrl = document.location.href.split('#')[0];
|
||||
|
||||
if (!baseUrl.startsWith('file://')) {
|
||||
newUrl = `${baseUrl}#${destination.hash}`;
|
||||
}
|
||||
}
|
||||
|
||||
if (shouldReplace) {
|
||||
window.history.replaceState(newState, '');
|
||||
window.history.replaceState(newState, '', newUrl);
|
||||
} else {
|
||||
this._maxUid = this._uid;
|
||||
window.history.pushState(newState, '');
|
||||
window.history.pushState(newState, '', newUrl);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -6023,13 +6045,25 @@ class PDFHistory {
|
|||
this._pushOrReplaceState(position, forceReplace);
|
||||
}
|
||||
|
||||
_isValidState(state) {
|
||||
_isValidState(state, checkReload = false) {
|
||||
if (!state) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (state.fingerprint !== this.fingerprint) {
|
||||
return false;
|
||||
if (checkReload) {
|
||||
if (typeof state.fingerprint !== 'string' || state.fingerprint.length !== this.fingerprint.length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const [perfEntry] = performance.getEntriesByType('navigation');
|
||||
|
||||
if (!perfEntry || perfEntry.type !== 'reload') {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!Number.isInteger(state.uid) || state.uid < 0) {
|
||||
|
@ -11600,6 +11634,7 @@ function getDefaultPreferences() {
|
|||
"disableOpenActionDestination": true,
|
||||
"disablePageMode": false,
|
||||
"disablePageLabels": false,
|
||||
"historyUpdateUrl": false,
|
||||
"scrollModeOnLoad": 0,
|
||||
"spreadModeOnLoad": 0
|
||||
});
|
||||
|
|
|
@ -20,7 +20,7 @@ origin:
|
|||
|
||||
# Human-readable identifier for this version/release
|
||||
# Generally "version NNN", "tag SSS", "bookmark SSS"
|
||||
release: version 2.1.153
|
||||
release: version 2.1.176
|
||||
|
||||
# The package's license, where possible using the mnemonic from
|
||||
# https://spdx.org/licenses/
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
{
|
||||
"presets": [
|
||||
"react",
|
||||
|
||||
[
|
||||
"env",
|
||||
{
|
||||
"targets": {
|
||||
"browsers": ["last 1 Chrome version", "last 1 Firefox version"]
|
||||
},
|
||||
"modules": "commonjs"
|
||||
}
|
||||
]
|
||||
],
|
||||
"plugins": [
|
||||
"transform-flow-strip-types",
|
||||
"transform-class-properties",
|
||||
"syntax-trailing-function-commas",
|
||||
"syntax-object-rest-spread",
|
||||
[
|
||||
"module-resolver",
|
||||
{
|
||||
"alias": {
|
||||
"devtools/client/shared/vendor/react": "react",
|
||||
"devtools/client/shared/vendor/react-dom": "react-dom"
|
||||
}
|
||||
}
|
||||
]
|
||||
],
|
||||
"env": {
|
||||
"test": {
|
||||
"presets": [
|
||||
[
|
||||
"env",
|
||||
{
|
||||
"targets": {
|
||||
"node": 7
|
||||
},
|
||||
"modules": "commonjs"
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,109 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
|
||||
|
||||
const React = require("react");
|
||||
import InlineSVG from "svg-inline-react";
|
||||
|
||||
const svg = {
|
||||
"angle-brackets": require("./angle-brackets.svg"),
|
||||
angular: require("./angular.svg"),
|
||||
arrow: require("./arrow.svg"),
|
||||
babel: require("./babel.svg"),
|
||||
backbone: require("./backbone.svg"),
|
||||
blackBox: require("./blackBox.svg"),
|
||||
breadcrumb: require("./breadcrumbs-divider.svg"),
|
||||
breakpoint: require("./breakpoint.svg"),
|
||||
"column-breakpoint": require("./column-breakpoint.svg"),
|
||||
"column-marker": require("./column-marker.svg"),
|
||||
"case-match": require("./case-match.svg"),
|
||||
choo: require("./choo.svg"),
|
||||
close: require("./close.svg"),
|
||||
coffeescript: require(`./coffeescript.svg`),
|
||||
dojo: require("./dojo.svg"),
|
||||
domain: require("./domain.svg"),
|
||||
extension: require("./extension.svg"),
|
||||
file: require("./file.svg"),
|
||||
folder: require("./folder.svg"),
|
||||
globe: require("./globe.svg"),
|
||||
home: require("./home.svg"),
|
||||
javascript: require("./javascript.svg"),
|
||||
jquery: require("./jquery.svg"),
|
||||
underscore: require("./underscore.svg"),
|
||||
lodash: require("./lodash.svg"),
|
||||
ember: require("./ember.svg"),
|
||||
vuejs: require("./vuejs.svg"),
|
||||
"magnifying-glass": require("./magnifying-glass.svg"),
|
||||
"arrow-up": require("./arrow-up.svg"),
|
||||
"arrow-down": require("./arrow-down.svg"),
|
||||
pause: require("./pause.svg"),
|
||||
"pause-exceptions": require("./pause-exceptions.svg"),
|
||||
plus: require("./plus.svg"),
|
||||
preact: require("./preact.svg"),
|
||||
aframe: require("./aframe.svg"),
|
||||
prettyPrint: require("./prettyPrint.svg"),
|
||||
react: require("./react.svg"),
|
||||
"regex-match": require("./regex-match.svg"),
|
||||
redux: require("./redux.svg"),
|
||||
immutable: require("./immutable.svg"),
|
||||
resume: require("./resume.svg"),
|
||||
settings: require("./settings.svg"),
|
||||
stepIn: require("./stepIn.svg"),
|
||||
stepOut: require("./stepOut.svg"),
|
||||
stepOver: require("./stepOver.svg"),
|
||||
subSettings: require("./subSettings.svg"),
|
||||
tab: require("./tab.svg"),
|
||||
toggleBreakpoints: require("./toggle-breakpoints.svg"),
|
||||
togglePanes: require("./toggle-panes.svg"),
|
||||
typescript: require("./typescript.svg"),
|
||||
"whole-word-match": require("./whole-word-match.svg"),
|
||||
worker: require("./worker.svg"),
|
||||
"sad-face": require("devtools-mc-assets/assets/devtools/client/themes/images/sad-face.svg"),
|
||||
refresh: require("devtools-mc-assets/assets/devtools/client/themes/images/reload.svg"),
|
||||
webpack: require("./webpack.svg"),
|
||||
node: require("./node.svg"),
|
||||
express: require("./express.svg"),
|
||||
pug: require("./pug.svg"),
|
||||
extjs: require("./sencha-extjs.svg"),
|
||||
mobx: require("./mobx.svg"),
|
||||
marko: require("./marko.svg"),
|
||||
nextjs: require("./nextjs.svg"),
|
||||
showSources: require("./showSources.svg"),
|
||||
showOutline: require("./showOutline.svg"),
|
||||
nuxtjs: require("./nuxtjs.svg"),
|
||||
rxjs: require("./rxjs.svg"),
|
||||
loader: require('./loader.svg')
|
||||
};
|
||||
|
||||
type SvgType = {
|
||||
name: string,
|
||||
className?: string,
|
||||
onClick?: () => void,
|
||||
"aria-label"?: string
|
||||
};
|
||||
|
||||
function Svg({ name, className, onClick, "aria-label": ariaLabel }) {
|
||||
if (!svg[name]) {
|
||||
const error = `Unknown SVG: ${name}`;
|
||||
console.warn(error);
|
||||
return null;
|
||||
}
|
||||
|
||||
className = `${name} ${className || ""}`;
|
||||
if (name === "subSettings") {
|
||||
className = "";
|
||||
}
|
||||
|
||||
const props = {
|
||||
className,
|
||||
onClick,
|
||||
["aria-label"]: ariaLabel,
|
||||
src: svg[name]
|
||||
};
|
||||
|
||||
return <InlineSVG {...props} />;
|
||||
}
|
||||
|
||||
Svg.displayName = "Svg";
|
||||
|
||||
module.exports = Svg;
|
|
@ -0,0 +1,40 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
|
||||
|
||||
const { resolve } = require("path");
|
||||
const rootDir = resolve(__dirname);
|
||||
module.exports = {
|
||||
rootDir,
|
||||
displayName: "test",
|
||||
testURL: "http://localhost/",
|
||||
testPathIgnorePatterns: [
|
||||
"/node_modules/",
|
||||
"/helpers/",
|
||||
"/fixtures/",
|
||||
"src/test/mochitest/examples/",
|
||||
"<rootDir>/firefox",
|
||||
"package.json",
|
||||
"<rootDir>/packages"
|
||||
],
|
||||
modulePathIgnorePatterns: ["src/test/mochitest", "firefox"],
|
||||
collectCoverageFrom: [
|
||||
"src/**/*.js",
|
||||
"!src/**/fixtures/*.js",
|
||||
"!src/test/**/*.js",
|
||||
"!src/components/stories/**/*.js",
|
||||
"!**/*.mock.js",
|
||||
"!**/*.spec.js"
|
||||
],
|
||||
transformIgnorePatterns: ["node_modules/(?!devtools-)"],
|
||||
setupTestFrameworkScriptFile: "<rootDir>/src/test/tests-setup.js",
|
||||
setupFiles: ["<rootDir>/src/test/shim.js", "jest-localstorage-mock"],
|
||||
snapshotSerializers: [
|
||||
"jest-serializer-babel-ast",
|
||||
"enzyme-to-json/serializer"
|
||||
],
|
||||
moduleNameMapper: {
|
||||
"\\.css$": "<rootDir>/src/test/__mocks__/styleMock.js",
|
||||
"\\.svg$": "<rootDir>/src/test/__mocks__/svgMock.js"
|
||||
}
|
||||
};
|
|
@ -0,0 +1,14 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
|
||||
|
||||
const { resolve } = require("path");
|
||||
const rootDir = resolve(__dirname);
|
||||
module.exports = {
|
||||
rootDir,
|
||||
reporters: ["default"],
|
||||
projects: [
|
||||
"<rootDir>/jest-test.config.js",
|
||||
"<rootDir>/packages/*/jest.config.js"
|
||||
]
|
||||
};
|
|
@ -0,0 +1,137 @@
|
|||
{
|
||||
"name": "debugger.html",
|
||||
"version": "0.6.0",
|
||||
"license": "MPL-2.0",
|
||||
"repository": {
|
||||
"url": "git://github.com/devtools-html/debugger.html.git",
|
||||
"type": "git"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/devtools-html/debugger.html/issues"
|
||||
},
|
||||
"homepage": "https://github.com/devtools-html/debugger.html#readme",
|
||||
"engineStrict": true,
|
||||
"engines": {
|
||||
"node": ">=7.7.0"
|
||||
},
|
||||
"scripts": {
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/core": "^7.0.0-beta.55",
|
||||
"@babel/parser": "^7.0.0-beta.55",
|
||||
"@babel/template": "^7.0.0-beta.55",
|
||||
"@babel/types": "^7.0.0-beta.55",
|
||||
"babel-plugin-transform-imports": "^1.5.0",
|
||||
"codemirror": "^5.28.0",
|
||||
"devtools-environment": "^0.0.6",
|
||||
"devtools-launchpad": "^0.0.141",
|
||||
"devtools-linters": "^0.0.4",
|
||||
"devtools-reps": "0.23.0",
|
||||
"devtools-source-map": "0.16.0",
|
||||
"devtools-splitter": "^0.0.8",
|
||||
"devtools-utils": "0.0.14",
|
||||
"fuzzaldrin-plus": "^0.6.0",
|
||||
"immutable": "^3.8.2",
|
||||
"lodash": "^4.17.4",
|
||||
"lodash-move": "^1.1.1",
|
||||
"lodash.kebabcase": "^4.1.1",
|
||||
"md5": "^2.2.1",
|
||||
"parse-script-tags": "^0.1.6",
|
||||
"pretty-fast": "^0.2.3",
|
||||
"prop-types": "^15.6.0",
|
||||
"react": "16.4.1",
|
||||
"react-aria-components": "^0.0.4",
|
||||
"react-dom": "16.4.1",
|
||||
"react-immutable-proptypes": "^2.1.0",
|
||||
"react-inlinesvg": "^0.8.1",
|
||||
"react-redux": "^5.0.7",
|
||||
"react-transition-group": "^2.2.1",
|
||||
"reselect": "^4.0.0",
|
||||
"svg-inline-react": "^3.0.0",
|
||||
"wasmparser": "^0.7.0"
|
||||
},
|
||||
"private": true,
|
||||
"workspaces": [
|
||||
"packages/*"
|
||||
],
|
||||
"files": [
|
||||
"src",
|
||||
"assets"
|
||||
],
|
||||
"greenkeeper": {
|
||||
"ignore": [
|
||||
"react",
|
||||
"react-dom",
|
||||
"react-redux",
|
||||
"redux",
|
||||
"codemirror"
|
||||
]
|
||||
},
|
||||
"main": "src/main.js",
|
||||
"author": "Jason Laster <jlaster@mozilla.com>",
|
||||
"devDependencies": {
|
||||
"@sucrase/webpack-object-rest-spread-plugin": "^1.0.0",
|
||||
"babel-eslint": "^10.0.1",
|
||||
"babel-jest": "^23.0.0",
|
||||
"babel-plugin-syntax-object-rest-spread": "^6.13.0",
|
||||
"babel-plugin-syntax-trailing-function-commas": "^6.22.0",
|
||||
"babel-plugin-transform-class-properties": "^6.24.1",
|
||||
"babel-plugin-transform-es2015-modules-commonjs": "^6.26.0",
|
||||
"babel-preset-env": "^1.6.1",
|
||||
"babel-preset-react": "^6.24.1",
|
||||
"chalk": "^2.1.0",
|
||||
"copy-paste": "^1.3.0",
|
||||
"copy-webpack-plugin": "^4.5.2",
|
||||
"devtools-license-check": "^0.7.0",
|
||||
"enzyme": "^3.3.0",
|
||||
"enzyme-adapter-react-16": "^1.1.1",
|
||||
"enzyme-to-json": "3.3.4",
|
||||
"eslint": "^5.0.0",
|
||||
"eslint-config-prettier": "^3.0.0",
|
||||
"eslint-plugin-babel": "^5.0.0",
|
||||
"eslint-plugin-file-header": "0.0.1",
|
||||
"eslint-plugin-flowtype": "^3.0.0",
|
||||
"eslint-plugin-import": "^2.8.0",
|
||||
"eslint-plugin-jest": "^21.15.1",
|
||||
"eslint-plugin-jsx-a11y": "^6.1.2",
|
||||
"eslint-plugin-mozilla": "1.0.4",
|
||||
"eslint-plugin-prettier": "^3.0.0",
|
||||
"eslint-plugin-react": "^7.2.1",
|
||||
"expect.js": "^0.3.1",
|
||||
"flow-bin": "^0.89.0",
|
||||
"glob": "^7.0.3",
|
||||
"husky": "^1.0.1",
|
||||
"jest": "^23.0.0",
|
||||
"jest-environment-jsdom": "^23.0.0",
|
||||
"jest-in-case": "^1.0.2",
|
||||
"jest-junit": "^5.0.0",
|
||||
"jest-localstorage-mock": "^2.2.0",
|
||||
"jest-serializer-babel-ast": "^0.0.5",
|
||||
"lint-staged": "^8.0.0",
|
||||
"mochii": "^0.0.32",
|
||||
"mock-require": "^3.0.0",
|
||||
"node-emoji": "^1.8.1",
|
||||
"npm-run-all": "^4.0.2",
|
||||
"prettier": "^1.12.1",
|
||||
"pretty-quick": "^1.4.1",
|
||||
"remark-cli": "^6.0.0",
|
||||
"remark-lint": "^6.0.1",
|
||||
"remark-lint-list-item-bullet-indent": "^1.0.1",
|
||||
"remark-lint-list-item-indent": "^1.0.1",
|
||||
"remark-lint-no-shortcut-reference-image": "^1.0.1",
|
||||
"remark-lint-no-shortcut-reference-link": "^1.0.2",
|
||||
"remark-lint-no-table-indentation": "^1.0.0",
|
||||
"remark-lint-no-unused-definitions": "^1.0.1",
|
||||
"remark-lint-ordered-list-marker-style": "^1.0.1",
|
||||
"remark-lint-table-cell-padding": "^1.0.0",
|
||||
"remark-lint-table-pipes": "^1.0.0",
|
||||
"remark-preset-lint-recommended": "^3.0.0",
|
||||
"remark-validate-links": "^7.0.0",
|
||||
"rimraf": "^2.6.1",
|
||||
"single-line-log": "^1.1.2",
|
||||
"stylelint": "^9.0.0",
|
||||
"webpack": "^3.5.5",
|
||||
"webpack-visualizer-plugin": "^0.1.11",
|
||||
"workerjs": "github:jasonLaster/workerjs#a2425aaeebacae7a7640e496a54c2a41962f3890"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"presets": ["react", "flow"],
|
||||
"plugins": [
|
||||
"transform-es2015-modules-commonjs",
|
||||
"transform-flow-strip-types"
|
||||
]
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
const { configure } = require("@storybook/react");
|
||||
|
||||
function loadStories() {
|
||||
require("../stories/index");
|
||||
}
|
||||
|
||||
configure(loadStories, module);
|
|
@ -0,0 +1,5 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
module.exports = require("../webpack.config.js");
|
|
@ -0,0 +1,362 @@
|
|||
Mozilla Public License, version 2.0
|
||||
|
||||
1. Definitions
|
||||
|
||||
1.1. "Contributor"
|
||||
|
||||
means each individual or legal entity that creates, contributes to the
|
||||
creation of, or owns Covered Software.
|
||||
|
||||
1.2. "Contributor Version"
|
||||
|
||||
means the combination of the Contributions of others (if any) used by a
|
||||
Contributor and that particular Contributor's Contribution.
|
||||
|
||||
1.3. "Contribution"
|
||||
|
||||
means Covered Software of a particular Contributor.
|
||||
|
||||
1.4. "Covered Software"
|
||||
|
||||
means Source Code Form to which the initial Contributor has attached the
|
||||
notice in Exhibit A, the Executable Form of such Source Code Form, and
|
||||
Modifications of such Source Code Form, in each case including portions
|
||||
thereof.
|
||||
|
||||
1.5. "Incompatible With Secondary Licenses"
|
||||
means
|
||||
|
||||
a. that the initial Contributor has attached the notice described in
|
||||
Exhibit B to the Covered Software; or
|
||||
|
||||
b. that the Covered Software was made available under the terms of
|
||||
version 1.1 or earlier of the License, but not also under the terms of
|
||||
a Secondary License.
|
||||
|
||||
1.6. "Executable Form"
|
||||
|
||||
means any form of the work other than Source Code Form.
|
||||
|
||||
1.7. "Larger Work"
|
||||
|
||||
means a work that combines Covered Software with other material, in a
|
||||
separate file or files, that is not Covered Software.
|
||||
|
||||
1.8. "License"
|
||||
|
||||
means this document.
|
||||
|
||||
1.9. "Licensable"
|
||||
|
||||
means having the right to grant, to the maximum extent possible, whether
|
||||
at the time of the initial grant or subsequently, any and all of the
|
||||
rights conveyed by this License.
|
||||
|
||||
1.10. "Modifications"
|
||||
|
||||
means any of the following:
|
||||
|
||||
a. any file in Source Code Form that results from an addition to,
|
||||
deletion from, or modification of the contents of Covered Software; or
|
||||
|
||||
b. any new file in Source Code Form that contains any Covered Software.
|
||||
|
||||
1.11. "Patent Claims" of a Contributor
|
||||
|
||||
means any patent claim(s), including without limitation, method,
|
||||
process, and apparatus claims, in any patent Licensable by such
|
||||
Contributor that would be infringed, but for the grant of the License,
|
||||
by the making, using, selling, offering for sale, having made, import,
|
||||
or transfer of either its Contributions or its Contributor Version.
|
||||
|
||||
1.12. "Secondary License"
|
||||
|
||||
means either the GNU General Public License, Version 2.0, the GNU Lesser
|
||||
General Public License, Version 2.1, the GNU Affero General Public
|
||||
License, Version 3.0, or any later versions of those licenses.
|
||||
|
||||
1.13. "Source Code Form"
|
||||
|
||||
means the form of the work preferred for making modifications.
|
||||
|
||||
1.14. "You" (or "Your")
|
||||
|
||||
means an individual or a legal entity exercising rights under this
|
||||
License. For legal entities, "You" includes any entity that controls, is
|
||||
controlled by, or is under common control with You. For purposes of this
|
||||
definition, "control" means (a) the power, direct or indirect, to cause
|
||||
the direction or management of such entity, whether by contract or
|
||||
otherwise, or (b) ownership of more than fifty percent (50%) of the
|
||||
outstanding shares or beneficial ownership of such entity.
|
||||
|
||||
|
||||
2. License Grants and Conditions
|
||||
|
||||
2.1. Grants
|
||||
|
||||
Each Contributor hereby grants You a world-wide, royalty-free,
|
||||
non-exclusive license:
|
||||
|
||||
a. under intellectual property rights (other than patent or trademark)
|
||||
Licensable by such Contributor to use, reproduce, make available,
|
||||
modify, display, perform, distribute, and otherwise exploit its
|
||||
Contributions, either on an unmodified basis, with Modifications, or
|
||||
as part of a Larger Work; and
|
||||
|
||||
b. under Patent Claims of such Contributor to make, use, sell, offer for
|
||||
sale, have made, import, and otherwise transfer either its
|
||||
Contributions or its Contributor Version.
|
||||
|
||||
2.2. Effective Date
|
||||
|
||||
The licenses granted in Section 2.1 with respect to any Contribution
|
||||
become effective for each Contribution on the date the Contributor first
|
||||
distributes such Contribution.
|
||||
|
||||
2.3. Limitations on Grant Scope
|
||||
|
||||
The licenses granted in this Section 2 are the only rights granted under
|
||||
this License. No additional rights or licenses will be implied from the
|
||||
distribution or licensing of Covered Software under this License.
|
||||
Notwithstanding Section 2.1(b) above, no patent license is granted by a
|
||||
Contributor:
|
||||
|
||||
a. for any code that a Contributor has removed from Covered Software; or
|
||||
|
||||
b. for infringements caused by: (i) Your and any other third party's
|
||||
modifications of Covered Software, or (ii) the combination of its
|
||||
Contributions with other software (except as part of its Contributor
|
||||
Version); or
|
||||
|
||||
c. under Patent Claims infringed by Covered Software in the absence of
|
||||
its Contributions.
|
||||
|
||||
This License does not grant any rights in the trademarks, service marks,
|
||||
or logos of any Contributor (except as may be necessary to comply with
|
||||
the notice requirements in Section 3.4).
|
||||
|
||||
2.4. Subsequent Licenses
|
||||
|
||||
No Contributor makes additional grants as a result of Your choice to
|
||||
distribute the Covered Software under a subsequent version of this
|
||||
License (see Section 10.2) or under the terms of a Secondary License (if
|
||||
permitted under the terms of Section 3.3).
|
||||
|
||||
2.5. Representation
|
||||
|
||||
Each Contributor represents that the Contributor believes its
|
||||
Contributions are its original creation(s) or it has sufficient rights to
|
||||
grant the rights to its Contributions conveyed by this License.
|
||||
|
||||
2.6. Fair Use
|
||||
|
||||
This License is not intended to limit any rights You have under
|
||||
applicable copyright doctrines of fair use, fair dealing, or other
|
||||
equivalents.
|
||||
|
||||
2.7. Conditions
|
||||
|
||||
Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in
|
||||
Section 2.1.
|
||||
|
||||
|
||||
3. Responsibilities
|
||||
|
||||
3.1. Distribution of Source Form
|
||||
|
||||
All distribution of Covered Software in Source Code Form, including any
|
||||
Modifications that You create or to which You contribute, must be under
|
||||
the terms of this License. You must inform recipients that the Source
|
||||
Code Form of the Covered Software is governed by the terms of this
|
||||
License, and how they can obtain a copy of this License. You may not
|
||||
attempt to alter or restrict the recipients' rights in the Source Code
|
||||
Form.
|
||||
|
||||
3.2. Distribution of Executable Form
|
||||
|
||||
If You distribute Covered Software in Executable Form then:
|
||||
|
||||
a. such Covered Software must also be made available in Source Code Form,
|
||||
as described in Section 3.1, and You must inform recipients of the
|
||||
Executable Form how they can obtain a copy of such Source Code Form by
|
||||
reasonable means in a timely manner, at a charge no more than the cost
|
||||
of distribution to the recipient; and
|
||||
|
||||
b. You may distribute such Executable Form under the terms of this
|
||||
License, or sublicense it under different terms, provided that the
|
||||
license for the Executable Form does not attempt to limit or alter the
|
||||
recipients' rights in the Source Code Form under this License.
|
||||
|
||||
3.3. Distribution of a Larger Work
|
||||
|
||||
You may create and distribute a Larger Work under terms of Your choice,
|
||||
provided that You also comply with the requirements of this License for
|
||||
the Covered Software. If the Larger Work is a combination of Covered
|
||||
Software with a work governed by one or more Secondary Licenses, and the
|
||||
Covered Software is not Incompatible With Secondary Licenses, this
|
||||
License permits You to additionally distribute such Covered Software
|
||||
under the terms of such Secondary License(s), so that the recipient of
|
||||
the Larger Work may, at their option, further distribute the Covered
|
||||
Software under the terms of either this License or such Secondary
|
||||
License(s).
|
||||
|
||||
3.4. Notices
|
||||
|
||||
You may not remove or alter the substance of any license notices
|
||||
(including copyright notices, patent notices, disclaimers of warranty, or
|
||||
limitations of liability) contained within the Source Code Form of the
|
||||
Covered Software, except that You may alter any license notices to the
|
||||
extent required to remedy known factual inaccuracies.
|
||||
|
||||
3.5. Application of Additional Terms
|
||||
|
||||
You may choose to offer, and to charge a fee for, warranty, support,
|
||||
indemnity or liability obligations to one or more recipients of Covered
|
||||
Software. However, You may do so only on Your own behalf, and not on
|
||||
behalf of any Contributor. You must make it absolutely clear that any
|
||||
such warranty, support, indemnity, or liability obligation is offered by
|
||||
You alone, and You hereby agree to indemnify every Contributor for any
|
||||
liability incurred by such Contributor as a result of warranty, support,
|
||||
indemnity or liability terms You offer. You may include additional
|
||||
disclaimers of warranty and limitations of liability specific to any
|
||||
jurisdiction.
|
||||
|
||||
4. Inability to Comply Due to Statute or Regulation
|
||||
|
||||
If it is impossible for You to comply with any of the terms of this License
|
||||
with respect to some or all of the Covered Software due to statute,
|
||||
judicial order, or regulation then You must: (a) comply with the terms of
|
||||
this License to the maximum extent possible; and (b) describe the
|
||||
limitations and the code they affect. Such description must be placed in a
|
||||
text file included with all distributions of the Covered Software under
|
||||
this License. Except to the extent prohibited by statute or regulation,
|
||||
such description must be sufficiently detailed for a recipient of ordinary
|
||||
skill to be able to understand it.
|
||||
|
||||
5. Termination
|
||||
|
||||
5.1. The rights granted under this License will terminate automatically if You
|
||||
fail to comply with any of its terms. However, if You become compliant,
|
||||
then the rights granted under this License from a particular Contributor
|
||||
are reinstated (a) provisionally, unless and until such Contributor
|
||||
explicitly and finally terminates Your grants, and (b) on an ongoing
|
||||
basis, if such Contributor fails to notify You of the non-compliance by
|
||||
some reasonable means prior to 60 days after You have come back into
|
||||
compliance. Moreover, Your grants from a particular Contributor are
|
||||
reinstated on an ongoing basis if such Contributor notifies You of the
|
||||
non-compliance by some reasonable means, this is the first time You have
|
||||
received notice of non-compliance with this License from such
|
||||
Contributor, and You become compliant prior to 30 days after Your receipt
|
||||
of the notice.
|
||||
|
||||
5.2. If You initiate litigation against any entity by asserting a patent
|
||||
infringement claim (excluding declaratory judgment actions,
|
||||
counter-claims, and cross-claims) alleging that a Contributor Version
|
||||
directly or indirectly infringes any patent, then the rights granted to
|
||||
You by any and all Contributors for the Covered Software under Section
|
||||
2.1 of this License shall terminate.
|
||||
|
||||
5.3. In the event of termination under Sections 5.1 or 5.2 above, all end user
|
||||
license agreements (excluding distributors and resellers) which have been
|
||||
validly granted by You or Your distributors under this License prior to
|
||||
termination shall survive termination.
|
||||
|
||||
6. Disclaimer of Warranty
|
||||
|
||||
Covered Software is provided under this License on an "as is" basis,
|
||||
without warranty of any kind, either expressed, implied, or statutory,
|
||||
including, without limitation, warranties that the Covered Software is free
|
||||
of defects, merchantable, fit for a particular purpose or non-infringing.
|
||||
The entire risk as to the quality and performance of the Covered Software
|
||||
is with You. Should any Covered Software prove defective in any respect,
|
||||
You (not any Contributor) assume the cost of any necessary servicing,
|
||||
repair, or correction. This disclaimer of warranty constitutes an essential
|
||||
part of this License. No use of any Covered Software is authorized under
|
||||
this License except under this disclaimer.
|
||||
|
||||
7. Limitation of Liability
|
||||
|
||||
Under no circumstances and under no legal theory, whether tort (including
|
||||
negligence), contract, or otherwise, shall any Contributor, or anyone who
|
||||
distributes Covered Software as permitted above, be liable to You for any
|
||||
direct, indirect, special, incidental, or consequential damages of any
|
||||
character including, without limitation, damages for lost profits, loss of
|
||||
goodwill, work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses, even if such party shall have been
|
||||
informed of the possibility of such damages. This limitation of liability
|
||||
shall not apply to liability for death or personal injury resulting from
|
||||
such party's negligence to the extent applicable law prohibits such
|
||||
limitation. Some jurisdictions do not allow the exclusion or limitation of
|
||||
incidental or consequential damages, so this exclusion and limitation may
|
||||
not apply to You.
|
||||
|
||||
8. Litigation
|
||||
|
||||
Any litigation relating to this License may be brought only in the courts
|
||||
of a jurisdiction where the defendant maintains its principal place of
|
||||
business and such litigation shall be governed by laws of that
|
||||
jurisdiction, without reference to its conflict-of-law provisions. Nothing
|
||||
in this Section shall prevent a party's ability to bring cross-claims or
|
||||
counter-claims.
|
||||
|
||||
9. Miscellaneous
|
||||
|
||||
This License represents the complete agreement concerning the subject
|
||||
matter hereof. If any provision of this License is held to be
|
||||
unenforceable, such provision shall be reformed only to the extent
|
||||
necessary to make it enforceable. Any law or regulation which provides that
|
||||
the language of a contract shall be construed against the drafter shall not
|
||||
be used to construe this License against a Contributor.
|
||||
|
||||
|
||||
10. Versions of the License
|
||||
|
||||
10.1. New Versions
|
||||
|
||||
Mozilla Foundation is the license steward. Except as provided in Section
|
||||
10.3, no one other than the license steward has the right to modify or
|
||||
publish new versions of this License. Each version will be given a
|
||||
distinguishing version number.
|
||||
|
||||
10.2. Effect of New Versions
|
||||
|
||||
You may distribute the Covered Software under the terms of the version
|
||||
of the License under which You originally received the Covered Software,
|
||||
or under the terms of any subsequent version published by the license
|
||||
steward.
|
||||
|
||||
10.3. Modified Versions
|
||||
|
||||
If you create software not governed by this License, and you want to
|
||||
create a new license for such software, you may create and use a
|
||||
modified version of this License if you rename the license and remove
|
||||
any references to the name of the license steward (except to note that
|
||||
such modified license differs from this License).
|
||||
|
||||
10.4. Distributing Source Code Form that is Incompatible With Secondary
|
||||
Licenses If You choose to distribute Source Code Form that is
|
||||
Incompatible With Secondary Licenses under the terms of this version of
|
||||
the License, the notice described in Exhibit B of this License must be
|
||||
attached.
|
||||
|
||||
Exhibit A - Source Code Form License Notice
|
||||
|
||||
This Source Code Form is subject to the
|
||||
terms of the Mozilla Public License, v.
|
||||
2.0. If a copy of the MPL was not
|
||||
distributed with this file, You can
|
||||
obtain one at
|
||||
http://mozilla.org/MPL/2.0/.
|
||||
|
||||
If it is not possible or desirable to put the notice in a particular file,
|
||||
then You may include the notice in a location (such as a LICENSE file in a
|
||||
relevant directory) where a recipient would be likely to look for such a
|
||||
notice.
|
||||
|
||||
You may add additional accurate notices of copyright ownership.
|
||||
|
||||
Exhibit B - "Incompatible With Secondary Licenses" Notice
|
||||
|
||||
This Source Code Form is "Incompatible
|
||||
With Secondary Licenses", as defined by
|
||||
the Mozilla Public License, v. 2.0.
|
|
@ -0,0 +1,5 @@
|
|||
## Devtools Components
|
||||
|
||||
Devtools shared Components
|
||||
|
||||
* _Tree_ - A performant tree
|
|
@ -0,0 +1,9 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
import Tree from "./src/tree";
|
||||
|
||||
module.exports = {
|
||||
Tree
|
||||
};
|
|
@ -0,0 +1,20 @@
|
|||
const { resolve } = require("path");
|
||||
const rootDir = resolve(__dirname, "src");
|
||||
module.exports = {
|
||||
rootDir,
|
||||
displayName: "devtools-components test",
|
||||
setupFiles: [
|
||||
"<rootDir>/../../../src/test/__mocks__/request-animation-frame.js",
|
||||
"<rootDir>/tests/setup.js"
|
||||
],
|
||||
testMatch: ["**/tests/**/*.js"],
|
||||
testPathIgnorePatterns: [
|
||||
"/node_modules/",
|
||||
"<rootDir>/tests/__mocks__/",
|
||||
"<rootDir>/tests/setup.js"
|
||||
],
|
||||
testURL: "http://localhost/",
|
||||
moduleNameMapper: {
|
||||
"\\.css$": "<rootDir>/../../../src/test/__mocks__/styleMock.js"
|
||||
}
|
||||
};
|
|
@ -0,0 +1,33 @@
|
|||
{
|
||||
"name": "devtools-components",
|
||||
"version": "0.6.0",
|
||||
"description": "DevTools HTML Components",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"copy-assets": "node bin/copy-assets",
|
||||
"license-check": "devtools-license-check",
|
||||
"storybook":
|
||||
"NODE_ENV=storybook start-storybook -p 9002 -c .storybook -s ./src",
|
||||
"test": "jest --projects jest.config.js"
|
||||
},
|
||||
"author": "Jason Laster",
|
||||
"license": "MPL-2.0",
|
||||
"dependencies": {
|
||||
"prop-types": "^15.6.0",
|
||||
"react": "^16.4.1",
|
||||
"react-dom": "^16.4.1",
|
||||
"react-dom-factories": "^1.0.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@storybook/react": "^3.3.14",
|
||||
"babel-plugin-transform-es2015-modules-commonjs": "^6.26.0",
|
||||
"babel-preset-react": "^6.24.1",
|
||||
"devtools-license-check": "^0.7.0",
|
||||
"enzyme": "^3.3.0",
|
||||
"enzyme-adapter-react-16": "^1.1.1",
|
||||
"eslint": "^5.0.0",
|
||||
"eslint-plugin-mozilla": "1.0.4",
|
||||
"fs-extra": "^7.0.0",
|
||||
"lodash": "^4.17.2"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
const mapUrl = require("postcss-url-mapper");
|
||||
const MC_PATH = "resource://devtools/client/debugger/new/images/";
|
||||
const EXPRESS_PATH = "/devtools-components/images/";
|
||||
|
||||
function mapUrlProduction(url, type) {
|
||||
return url.replace("/images/arrow.svg", MC_PATH + "arrow.svg");
|
||||
}
|
||||
|
||||
function mapUrlDevelopment(url) {
|
||||
return url.replace("/images/arrow.svg", EXPRESS_PATH + "arrow.svg");
|
||||
}
|
||||
|
||||
module.exports = ({ file, options, env }) => {
|
||||
// Here we don't want to do anything for storybook since we serve the images thanks
|
||||
// to the `-s ./src` option in the `storybook` command (see package.json).
|
||||
if (env === "storybook") {
|
||||
return {};
|
||||
}
|
||||
|
||||
// This will be used when creating a bundle for mozilla-central (from devtools-reps
|
||||
// or debugger.html).
|
||||
if (env === "production") {
|
||||
return {
|
||||
plugins: [mapUrl(mapUrlProduction)]
|
||||
};
|
||||
}
|
||||
|
||||
// This will be used when using this module in launchpad mode. We set a unique path so
|
||||
// we can serve images from express.
|
||||
return {
|
||||
plugins: [mapUrl(mapUrlDevelopment)]
|
||||
};
|
||||
};
|
|
@ -0,0 +1,6 @@
|
|||
<!-- This Source Code Form is subject to the terms of the Mozilla Public
|
||||
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16" fill="context-fill #9B9B9B">
|
||||
<path d="M8 13.4c-.5 0-.9-.2-1.2-.6L.4 5.2C0 4.7-.1 4.3.2 3.7S1 3 1.6 3h12.8c.6 0 1.2.1 1.4.7.3.6.2 1.1-.2 1.6l-6.4 7.6c-.3.4-.7.5-1.2.5z"/>
|
||||
</svg>
|
После Ширина: | Высота: | Размер: 474 B |
|
@ -0,0 +1,908 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`Tree Don't auto expand root with very large number of children 1`] = `
|
||||
Array [
|
||||
"key-A",
|
||||
"key-B",
|
||||
"key-E",
|
||||
"key-F",
|
||||
"key-G",
|
||||
"key-C",
|
||||
"key-H",
|
||||
"key-I",
|
||||
"key-D",
|
||||
"key-J",
|
||||
"key-M",
|
||||
"key-N",
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`Tree ignores key strokes when pressing modifiers 1`] = `
|
||||
"
|
||||
▼ A
|
||||
| ▼ B
|
||||
| | ▼ E
|
||||
| | | K
|
||||
| | | [L]
|
||||
| | F
|
||||
| | G
|
||||
| ▼ C
|
||||
| | H
|
||||
| | I
|
||||
| ▼ D
|
||||
| | J
|
||||
▼ M
|
||||
| ▼ N
|
||||
| | O
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`Tree ignores key strokes when pressing modifiers 2`] = `
|
||||
"
|
||||
▼ A
|
||||
| ▼ B
|
||||
| | ▼ E
|
||||
| | | K
|
||||
| | | [L]
|
||||
| | F
|
||||
| | G
|
||||
| ▼ C
|
||||
| | H
|
||||
| | I
|
||||
| ▼ D
|
||||
| | J
|
||||
▼ M
|
||||
| ▼ N
|
||||
| | O
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`Tree ignores key strokes when pressing modifiers 3`] = `
|
||||
"
|
||||
▼ A
|
||||
| ▼ B
|
||||
| | ▼ E
|
||||
| | | K
|
||||
| | | [L]
|
||||
| | F
|
||||
| | G
|
||||
| ▼ C
|
||||
| | H
|
||||
| | I
|
||||
| ▼ D
|
||||
| | J
|
||||
▼ M
|
||||
| ▼ N
|
||||
| | O
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`Tree ignores key strokes when pressing modifiers 4`] = `
|
||||
"
|
||||
▼ A
|
||||
| ▼ B
|
||||
| | ▼ E
|
||||
| | | K
|
||||
| | | [L]
|
||||
| | F
|
||||
| | G
|
||||
| ▼ C
|
||||
| | H
|
||||
| | I
|
||||
| ▼ D
|
||||
| | J
|
||||
▼ M
|
||||
| ▼ N
|
||||
| | O
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`Tree ignores key strokes when pressing modifiers 5`] = `
|
||||
"
|
||||
▼ A
|
||||
| ▼ B
|
||||
| | ▼ E
|
||||
| | | K
|
||||
| | | [L]
|
||||
| | F
|
||||
| | G
|
||||
| ▼ C
|
||||
| | H
|
||||
| | I
|
||||
| ▼ D
|
||||
| | J
|
||||
▼ M
|
||||
| ▼ N
|
||||
| | O
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`Tree ignores key strokes when pressing modifiers 6`] = `
|
||||
"
|
||||
▼ A
|
||||
| ▼ B
|
||||
| | ▼ E
|
||||
| | | K
|
||||
| | | [L]
|
||||
| | F
|
||||
| | G
|
||||
| ▼ C
|
||||
| | H
|
||||
| | I
|
||||
| ▼ D
|
||||
| | J
|
||||
▼ M
|
||||
| ▼ N
|
||||
| | O
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`Tree ignores key strokes when pressing modifiers 7`] = `
|
||||
"
|
||||
▼ A
|
||||
| ▼ B
|
||||
| | ▼ E
|
||||
| | | K
|
||||
| | | [L]
|
||||
| | F
|
||||
| | G
|
||||
| ▼ C
|
||||
| | H
|
||||
| | I
|
||||
| ▼ D
|
||||
| | J
|
||||
▼ M
|
||||
| ▼ N
|
||||
| | O
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`Tree ignores key strokes when pressing modifiers 8`] = `
|
||||
"
|
||||
▼ A
|
||||
| ▼ B
|
||||
| | ▼ E
|
||||
| | | K
|
||||
| | | [L]
|
||||
| | F
|
||||
| | G
|
||||
| ▼ C
|
||||
| | H
|
||||
| | I
|
||||
| ▼ D
|
||||
| | J
|
||||
▼ M
|
||||
| ▼ N
|
||||
| | O
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`Tree ignores key strokes when pressing modifiers 9`] = `
|
||||
"
|
||||
▼ A
|
||||
| ▼ B
|
||||
| | ▼ E
|
||||
| | | K
|
||||
| | | [L]
|
||||
| | F
|
||||
| | G
|
||||
| ▼ C
|
||||
| | H
|
||||
| | I
|
||||
| ▼ D
|
||||
| | J
|
||||
▼ M
|
||||
| ▼ N
|
||||
| | O
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`Tree ignores key strokes when pressing modifiers 10`] = `
|
||||
"
|
||||
▼ A
|
||||
| ▼ B
|
||||
| | ▼ E
|
||||
| | | K
|
||||
| | | [L]
|
||||
| | F
|
||||
| | G
|
||||
| ▼ C
|
||||
| | H
|
||||
| | I
|
||||
| ▼ D
|
||||
| | J
|
||||
▼ M
|
||||
| ▼ N
|
||||
| | O
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`Tree ignores key strokes when pressing modifiers 11`] = `
|
||||
"
|
||||
▼ A
|
||||
| ▼ B
|
||||
| | ▼ E
|
||||
| | | K
|
||||
| | | [L]
|
||||
| | F
|
||||
| | G
|
||||
| ▼ C
|
||||
| | H
|
||||
| | I
|
||||
| ▼ D
|
||||
| | J
|
||||
▼ M
|
||||
| ▼ N
|
||||
| | O
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`Tree ignores key strokes when pressing modifiers 12`] = `
|
||||
"
|
||||
▼ A
|
||||
| ▼ B
|
||||
| | ▼ E
|
||||
| | | K
|
||||
| | | [L]
|
||||
| | F
|
||||
| | G
|
||||
| ▼ C
|
||||
| | H
|
||||
| | I
|
||||
| ▼ D
|
||||
| | J
|
||||
▼ M
|
||||
| ▼ N
|
||||
| | O
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`Tree ignores key strokes when pressing modifiers 13`] = `
|
||||
"
|
||||
▼ A
|
||||
| ▼ B
|
||||
| | ▼ E
|
||||
| | | K
|
||||
| | | [L]
|
||||
| | F
|
||||
| | G
|
||||
| ▼ C
|
||||
| | H
|
||||
| | I
|
||||
| ▼ D
|
||||
| | J
|
||||
▼ M
|
||||
| ▼ N
|
||||
| | O
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`Tree ignores key strokes when pressing modifiers 14`] = `
|
||||
"
|
||||
▼ A
|
||||
| ▼ B
|
||||
| | ▼ E
|
||||
| | | K
|
||||
| | | [L]
|
||||
| | F
|
||||
| | G
|
||||
| ▼ C
|
||||
| | H
|
||||
| | I
|
||||
| ▼ D
|
||||
| | J
|
||||
▼ M
|
||||
| ▼ N
|
||||
| | O
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`Tree ignores key strokes when pressing modifiers 15`] = `
|
||||
"
|
||||
▼ A
|
||||
| ▼ B
|
||||
| | ▼ E
|
||||
| | | K
|
||||
| | | [L]
|
||||
| | F
|
||||
| | G
|
||||
| ▼ C
|
||||
| | H
|
||||
| | I
|
||||
| ▼ D
|
||||
| | J
|
||||
▼ M
|
||||
| ▼ N
|
||||
| | O
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`Tree ignores key strokes when pressing modifiers 16`] = `
|
||||
"
|
||||
▼ A
|
||||
| ▼ B
|
||||
| | ▼ E
|
||||
| | | K
|
||||
| | | [L]
|
||||
| | F
|
||||
| | G
|
||||
| ▼ C
|
||||
| | H
|
||||
| | I
|
||||
| ▼ D
|
||||
| | J
|
||||
▼ M
|
||||
| ▼ N
|
||||
| | O
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`Tree ignores key strokes when pressing modifiers 17`] = `
|
||||
"
|
||||
▼ A
|
||||
| ▼ B
|
||||
| | ▼ E
|
||||
| | | K
|
||||
| | | [L]
|
||||
| | F
|
||||
| | G
|
||||
| ▼ C
|
||||
| | H
|
||||
| | I
|
||||
| ▼ D
|
||||
| | J
|
||||
▼ M
|
||||
| ▼ N
|
||||
| | O
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`Tree renders arrows as expected when nodes are collapsed 1`] = `
|
||||
"
|
||||
▶︎ A
|
||||
▶︎ M
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`Tree renders arrows as expected when nodes are expanded 1`] = `
|
||||
"
|
||||
▼ A
|
||||
| ▼ B
|
||||
| | ▼ E
|
||||
| | | K
|
||||
| | | L
|
||||
| | F
|
||||
| | G
|
||||
| ▼ C
|
||||
| | H
|
||||
| | I
|
||||
| ▼ D
|
||||
| | J
|
||||
▼ M
|
||||
| ▼ N
|
||||
| | O
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`Tree renders as expected 1`] = `
|
||||
"
|
||||
▼ A
|
||||
| ▼ B
|
||||
| | ▼ E
|
||||
| | | K
|
||||
| | | L
|
||||
| | F
|
||||
| | G
|
||||
| ▼ C
|
||||
| | H
|
||||
| | I
|
||||
| ▼ D
|
||||
| | J
|
||||
▼ M
|
||||
| ▼ N
|
||||
| | O
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`Tree renders as expected navigating down with keyboard on last node 1`] = `
|
||||
"
|
||||
▼ A
|
||||
| ▼ B
|
||||
| | ▼ E
|
||||
| | | K
|
||||
| | | L
|
||||
| | F
|
||||
| | G
|
||||
| ▼ C
|
||||
| | H
|
||||
| | I
|
||||
| ▼ D
|
||||
| | J
|
||||
▼ M
|
||||
| ▼ N
|
||||
| | [O]
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`Tree renders as expected navigating down with keyboard on last node 2`] = `
|
||||
"
|
||||
▼ A
|
||||
| ▼ B
|
||||
| | ▼ E
|
||||
| | | K
|
||||
| | | L
|
||||
| | F
|
||||
| | G
|
||||
| ▼ C
|
||||
| | H
|
||||
| | I
|
||||
| ▼ D
|
||||
| | J
|
||||
▼ M
|
||||
| ▼ N
|
||||
| | [O]
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`Tree renders as expected navigating up with the keyboard on a root 1`] = `
|
||||
"
|
||||
▼ [A]
|
||||
| ▼ B
|
||||
| | ▼ E
|
||||
| | | K
|
||||
| | | L
|
||||
| | F
|
||||
| | G
|
||||
| ▼ C
|
||||
| | H
|
||||
| | I
|
||||
| ▼ D
|
||||
| | J
|
||||
▼ M
|
||||
| ▼ N
|
||||
| | O
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`Tree renders as expected navigating up with the keyboard on a root 2`] = `
|
||||
"
|
||||
▼ [A]
|
||||
| ▼ B
|
||||
| | ▼ E
|
||||
| | | K
|
||||
| | | L
|
||||
| | F
|
||||
| | G
|
||||
| ▼ C
|
||||
| | H
|
||||
| | I
|
||||
| ▼ D
|
||||
| | J
|
||||
▼ M
|
||||
| ▼ N
|
||||
| | O
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`Tree renders as expected navigating with arrows on unexpandable roots 1`] = `
|
||||
"
|
||||
[A]
|
||||
M
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`Tree renders as expected navigating with arrows on unexpandable roots 2`] = `
|
||||
"
|
||||
A
|
||||
[M]
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`Tree renders as expected navigating with arrows on unexpandable roots 3`] = `
|
||||
"
|
||||
[A]
|
||||
M
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`Tree renders as expected when given a focused item 1`] = `
|
||||
"
|
||||
▼ A
|
||||
| ▼ B
|
||||
| | ▼ E
|
||||
| | | K
|
||||
| | | L
|
||||
| | F
|
||||
| | [G]
|
||||
| ▼ C
|
||||
| | H
|
||||
| | I
|
||||
| ▼ D
|
||||
| | J
|
||||
▼ M
|
||||
| ▼ N
|
||||
| | O
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`Tree renders as expected when given a focused item 2`] = `
|
||||
"
|
||||
▶︎ [A]
|
||||
▼ M
|
||||
| ▼ N
|
||||
| | O
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`Tree renders as expected when given a focused item 3`] = `
|
||||
"
|
||||
▼ [A]
|
||||
| ▼ B
|
||||
| | ▼ E
|
||||
| | | K
|
||||
| | | L
|
||||
| | F
|
||||
| | G
|
||||
| ▼ C
|
||||
| | H
|
||||
| | I
|
||||
| ▼ D
|
||||
| | J
|
||||
▼ M
|
||||
| ▼ N
|
||||
| | O
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`Tree renders as expected when given a focused item 4`] = `
|
||||
"
|
||||
▼ A
|
||||
| ▼ B
|
||||
| | ▼ E
|
||||
| | | K
|
||||
| | | L
|
||||
| | F
|
||||
| | G
|
||||
| ▼ C
|
||||
| | H
|
||||
| | I
|
||||
| ▼ D
|
||||
| | J
|
||||
▼ M
|
||||
| ▼ N
|
||||
| | O
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`Tree renders as expected when navigating down with the keyboard 1`] = `
|
||||
"
|
||||
▼ A
|
||||
| ▼ B
|
||||
| | ▼ E
|
||||
| | | [K]
|
||||
| | | L
|
||||
| | F
|
||||
| | G
|
||||
| ▼ C
|
||||
| | H
|
||||
| | I
|
||||
| ▼ D
|
||||
| | J
|
||||
▼ M
|
||||
| ▼ N
|
||||
| | O
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`Tree renders as expected when navigating down with the keyboard 2`] = `
|
||||
"
|
||||
▼ A
|
||||
| ▼ B
|
||||
| | ▼ E
|
||||
| | | K
|
||||
| | | [L]
|
||||
| | F
|
||||
| | G
|
||||
| ▼ C
|
||||
| | H
|
||||
| | I
|
||||
| ▼ D
|
||||
| | J
|
||||
▼ M
|
||||
| ▼ N
|
||||
| | O
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`Tree renders as expected when navigating down with the keyboard 3`] = `
|
||||
"
|
||||
▼ A
|
||||
| ▼ B
|
||||
| | ▼ E
|
||||
| | | K
|
||||
| | | L
|
||||
| | [F]
|
||||
| | G
|
||||
| ▼ C
|
||||
| | H
|
||||
| | I
|
||||
| ▼ D
|
||||
| | J
|
||||
▼ M
|
||||
| ▼ N
|
||||
| | O
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`Tree renders as expected when navigating up with the keyboard 1`] = `
|
||||
"
|
||||
▼ A
|
||||
| ▼ B
|
||||
| | ▼ E
|
||||
| | | K
|
||||
| | | [L]
|
||||
| | F
|
||||
| | G
|
||||
| ▼ C
|
||||
| | H
|
||||
| | I
|
||||
| ▼ D
|
||||
| | J
|
||||
▼ M
|
||||
| ▼ N
|
||||
| | O
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`Tree renders as expected when navigating up with the keyboard 2`] = `
|
||||
"
|
||||
▼ A
|
||||
| ▼ B
|
||||
| | ▼ E
|
||||
| | | [K]
|
||||
| | | L
|
||||
| | F
|
||||
| | G
|
||||
| ▼ C
|
||||
| | H
|
||||
| | I
|
||||
| ▼ D
|
||||
| | J
|
||||
▼ M
|
||||
| ▼ N
|
||||
| | O
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`Tree renders as expected when navigating up with the keyboard 3`] = `
|
||||
"
|
||||
▼ A
|
||||
| ▼ B
|
||||
| | ▼ [E]
|
||||
| | | K
|
||||
| | | L
|
||||
| | F
|
||||
| | G
|
||||
| ▼ C
|
||||
| | H
|
||||
| | I
|
||||
| ▼ D
|
||||
| | J
|
||||
▼ M
|
||||
| ▼ N
|
||||
| | O
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`Tree renders as expected when navigating with home/end 1`] = `
|
||||
"
|
||||
▶︎ A
|
||||
▶︎ [M]
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`Tree renders as expected when navigating with home/end 2`] = `
|
||||
"
|
||||
▶︎ [A]
|
||||
▶︎ M
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`Tree renders as expected when navigating with home/end 3`] = `
|
||||
"
|
||||
▶︎ [A]
|
||||
▶︎ M
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`Tree renders as expected when navigating with home/end 4`] = `
|
||||
"
|
||||
▶︎ A
|
||||
▶︎ [M]
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`Tree renders as expected when navigating with home/end 5`] = `
|
||||
"
|
||||
▶︎ A
|
||||
▶︎ [M]
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`Tree renders as expected when navigating with home/end 6`] = `
|
||||
"
|
||||
▶︎ A
|
||||
▼ [M]
|
||||
| ▶︎ N
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`Tree renders as expected when navigating with home/end 7`] = `
|
||||
"
|
||||
▶︎ A
|
||||
▼ M
|
||||
| ▶︎ [N]
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`Tree renders as expected when navigating with home/end 8`] = `
|
||||
"
|
||||
▶︎ A
|
||||
▼ M
|
||||
| ▶︎ [N]
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`Tree renders as expected when navigating with home/end 9`] = `
|
||||
"
|
||||
▶︎ [A]
|
||||
▼ M
|
||||
| ▶︎ N
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`Tree renders as expected when navigating with left arrows on roots 1`] = `
|
||||
"
|
||||
▶︎ A
|
||||
▶︎ [M]
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`Tree renders as expected when navigating with left arrows on roots 2`] = `
|
||||
"
|
||||
▶︎ [A]
|
||||
▶︎ M
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`Tree renders as expected when navigating with left arrows on roots 3`] = `
|
||||
"
|
||||
▶︎ [A]
|
||||
▶︎ M
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`Tree renders as expected when navigating with right/left arrows 1`] = `
|
||||
"
|
||||
▼ A
|
||||
| ▼ B
|
||||
| | ▼ E
|
||||
| | | K
|
||||
| | | [L]
|
||||
| | F
|
||||
| | G
|
||||
| ▼ C
|
||||
| | H
|
||||
| | I
|
||||
| ▼ D
|
||||
| | J
|
||||
▼ M
|
||||
| ▼ N
|
||||
| | O
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`Tree renders as expected when navigating with right/left arrows 2`] = `
|
||||
"
|
||||
▼ A
|
||||
| ▼ B
|
||||
| | ▼ [E]
|
||||
| | | K
|
||||
| | | L
|
||||
| | F
|
||||
| | G
|
||||
| ▼ C
|
||||
| | H
|
||||
| | I
|
||||
| ▼ D
|
||||
| | J
|
||||
▼ M
|
||||
| ▼ N
|
||||
| | O
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`Tree renders as expected when navigating with right/left arrows 3`] = `
|
||||
"
|
||||
▼ A
|
||||
| ▼ B
|
||||
| | ▶︎ [E]
|
||||
| | F
|
||||
| | G
|
||||
| ▼ C
|
||||
| | H
|
||||
| | I
|
||||
| ▼ D
|
||||
| | J
|
||||
▼ M
|
||||
| ▼ N
|
||||
| | O
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`Tree renders as expected when navigating with right/left arrows 4`] = `
|
||||
"
|
||||
▼ A
|
||||
| ▼ B
|
||||
| | ▼ [E]
|
||||
| | | K
|
||||
| | | L
|
||||
| | F
|
||||
| | G
|
||||
| ▼ C
|
||||
| | H
|
||||
| | I
|
||||
| ▼ D
|
||||
| | J
|
||||
▼ M
|
||||
| ▼ N
|
||||
| | O
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`Tree renders as expected when navigating with right/left arrows 5`] = `
|
||||
"
|
||||
▼ A
|
||||
| ▼ B
|
||||
| | ▼ E
|
||||
| | | [K]
|
||||
| | | L
|
||||
| | F
|
||||
| | G
|
||||
| ▼ C
|
||||
| | H
|
||||
| | I
|
||||
| ▼ D
|
||||
| | J
|
||||
▼ M
|
||||
| ▼ N
|
||||
| | O
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`Tree renders as expected when passed autoDepth:1 1`] = `
|
||||
"
|
||||
▼ A
|
||||
| ▶︎ B
|
||||
| ▶︎ C
|
||||
| ▶︎ D
|
||||
▼ M
|
||||
| ▶︎ N
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`Tree renders as expected with collapsed nodes 1`] = `
|
||||
"
|
||||
▶︎ A
|
||||
▼ M
|
||||
| ▼ N
|
||||
| | O
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`Tree uses isExpandable prop if it exists to render tree nodes 1`] = `
|
||||
"
|
||||
▶︎ A
|
||||
M
|
||||
"
|
||||
`;
|
|
@ -0,0 +1,8 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
|
||||
|
||||
// Configure enzyme with React 16 adapter.
|
||||
const Enzyme = require("enzyme");
|
||||
const Adapter = require("enzyme-adapter-react-16");
|
||||
Enzyme.configure({ adapter: new Adapter() });
|
|
@ -0,0 +1,762 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
|
||||
|
||||
/* global jest */
|
||||
|
||||
import React from "react";
|
||||
import { mount } from "enzyme";
|
||||
import Components from "../../index";
|
||||
import dom from "react-dom-factories";
|
||||
|
||||
const { Component, createFactory } = React;
|
||||
const Tree = createFactory(Components.Tree);
|
||||
|
||||
function mountTree(overrides = {}) {
|
||||
return mount(
|
||||
createFactory(
|
||||
class container extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
const state = {
|
||||
expanded: overrides.expanded || new Set(),
|
||||
focused: overrides.focused
|
||||
};
|
||||
delete overrides.focused;
|
||||
this.state = state;
|
||||
}
|
||||
|
||||
render() {
|
||||
return Tree(
|
||||
Object.assign(
|
||||
{
|
||||
getParent: x => TEST_TREE.parent[x],
|
||||
getChildren: x => TEST_TREE.children[x],
|
||||
renderItem: (x, depth, focused, arrow) => {
|
||||
return dom.div(
|
||||
{},
|
||||
arrow,
|
||||
focused ? "[" : null,
|
||||
x,
|
||||
focused ? "]" : null
|
||||
);
|
||||
},
|
||||
getRoots: () => ["A", "M"],
|
||||
getKey: x => `key-${x}`,
|
||||
itemHeight: 1,
|
||||
onFocus: x => {
|
||||
this.setState(previousState => {
|
||||
return { focused: x };
|
||||
});
|
||||
},
|
||||
onExpand: x => {
|
||||
this.setState(previousState => {
|
||||
const expanded = new Set(previousState.expanded);
|
||||
expanded.add(x);
|
||||
return { expanded };
|
||||
});
|
||||
},
|
||||
onCollapse: x => {
|
||||
this.setState(previousState => {
|
||||
const expanded = new Set(previousState.expanded);
|
||||
expanded.delete(x);
|
||||
return { expanded };
|
||||
});
|
||||
},
|
||||
isExpanded: x => this.state && this.state.expanded.has(x),
|
||||
focused: this.state.focused
|
||||
},
|
||||
overrides
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
)()
|
||||
);
|
||||
}
|
||||
|
||||
describe("Tree", () => {
|
||||
it("does not throw", () => {
|
||||
expect(mountTree()).toBeTruthy();
|
||||
});
|
||||
|
||||
it("Don't auto expand root with very large number of children", () => {
|
||||
const children = Array.from(
|
||||
{ length: 51 },
|
||||
(_, i) => `should-not-be-visible-${i + 1}`
|
||||
);
|
||||
// N has a lot of children, so it won't be automatically expanded
|
||||
const wrapper = mountTree({
|
||||
autoExpandDepth: 2,
|
||||
autoExpandNodeChildrenLimit: 50,
|
||||
getChildren: item => {
|
||||
if (item === "N") {
|
||||
return children;
|
||||
}
|
||||
|
||||
return TEST_TREE.children[item] || [];
|
||||
}
|
||||
});
|
||||
const ids = getTreeNodes(wrapper).map(node => node.prop("id"));
|
||||
expect(ids).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("is accessible", () => {
|
||||
const wrapper = mountTree({
|
||||
expanded: new Set("ABCDEFGHIJMN".split(""))
|
||||
});
|
||||
expect(wrapper.getDOMNode().getAttribute("role")).toBe("tree");
|
||||
expect(wrapper.getDOMNode().getAttribute("tabIndex")).toBe("0");
|
||||
|
||||
const expected = {
|
||||
A: { id: "key-A", level: 1, expanded: true },
|
||||
B: { id: "key-B", level: 2, expanded: true },
|
||||
C: { id: "key-C", level: 2, expanded: true },
|
||||
D: { id: "key-D", level: 2, expanded: true },
|
||||
E: { id: "key-E", level: 3, expanded: true },
|
||||
F: { id: "key-F", level: 3, expanded: true },
|
||||
G: { id: "key-G", level: 3, expanded: true },
|
||||
H: { id: "key-H", level: 3, expanded: true },
|
||||
I: { id: "key-I", level: 3, expanded: true },
|
||||
J: { id: "key-J", level: 3, expanded: true },
|
||||
K: { id: "key-K", level: 4, expanded: undefined },
|
||||
L: { id: "key-L", level: 4, expanded: undefined },
|
||||
M: { id: "key-M", level: 1, expanded: true },
|
||||
N: { id: "key-N", level: 2, expanded: true },
|
||||
O: { id: "key-O", level: 3, expanded: undefined }
|
||||
};
|
||||
|
||||
getTreeNodes(wrapper).forEach(node => {
|
||||
const key = node.prop("id").replace("key-", "");
|
||||
const item = expected[key];
|
||||
|
||||
expect(node.prop("id")).toBe(item.id);
|
||||
expect(node.prop("role")).toBe("treeitem");
|
||||
expect(node.prop("aria-level")).toBe(item.level);
|
||||
expect(node.prop("aria-expanded")).toBe(item.expanded);
|
||||
});
|
||||
});
|
||||
|
||||
it("renders as expected", () => {
|
||||
const wrapper = mountTree({
|
||||
expanded: new Set("ABCDEFGHIJKLMNO".split(""))
|
||||
});
|
||||
|
||||
expect(formatTree(wrapper)).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("renders as expected when passed a className", () => {
|
||||
const wrapper = mountTree({
|
||||
className: "testClassName"
|
||||
});
|
||||
|
||||
expect(wrapper.find(".tree").hasClass("testClassName")).toBe(true);
|
||||
});
|
||||
|
||||
it("renders as expected when passed a style", () => {
|
||||
const wrapper = mountTree({
|
||||
style: {
|
||||
color: "red"
|
||||
}
|
||||
});
|
||||
|
||||
expect(wrapper.getDOMNode().style.color).toBe("red");
|
||||
});
|
||||
|
||||
it("renders as expected when passed a label", () => {
|
||||
const wrapper = mountTree({
|
||||
label: "testAriaLabel"
|
||||
});
|
||||
expect(wrapper.getDOMNode().getAttribute("aria-label")).toBe(
|
||||
"testAriaLabel"
|
||||
);
|
||||
});
|
||||
|
||||
it("renders as expected when passed an aria-labelledby", () => {
|
||||
const wrapper = mountTree({
|
||||
labelledby: "testAriaLabelBy"
|
||||
});
|
||||
expect(wrapper.getDOMNode().getAttribute("aria-labelledby")).toBe(
|
||||
"testAriaLabelBy"
|
||||
);
|
||||
});
|
||||
|
||||
it("renders as expected with collapsed nodes", () => {
|
||||
const wrapper = mountTree({
|
||||
expanded: new Set("MNO".split(""))
|
||||
});
|
||||
expect(formatTree(wrapper)).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("renders as expected when passed autoDepth:1", () => {
|
||||
const wrapper = mountTree({
|
||||
autoExpandDepth: 1
|
||||
});
|
||||
expect(formatTree(wrapper)).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("renders as expected when given a focused item", () => {
|
||||
const wrapper = mountTree({
|
||||
expanded: new Set("ABCDEFGHIJKLMNO".split("")),
|
||||
focused: "G"
|
||||
});
|
||||
expect(formatTree(wrapper)).toMatchSnapshot();
|
||||
expect(wrapper.getDOMNode().getAttribute("aria-activedescendant")).toBe(
|
||||
"key-G"
|
||||
);
|
||||
expect(wrapper.find(".focused").prop("id")).toBe("key-G");
|
||||
|
||||
getTreeNodes(wrapper)
|
||||
.first()
|
||||
.simulate("click");
|
||||
expect(formatTree(wrapper)).toMatchSnapshot();
|
||||
expect(wrapper.getDOMNode().getAttribute("aria-activedescendant")).toBe(
|
||||
"key-A"
|
||||
);
|
||||
expect(wrapper.find(".focused").prop("id")).toBe("key-A");
|
||||
|
||||
getTreeNodes(wrapper)
|
||||
.first()
|
||||
.simulate("click");
|
||||
expect(formatTree(wrapper)).toMatchSnapshot();
|
||||
expect(wrapper.getDOMNode().getAttribute("aria-activedescendant")).toBe(
|
||||
"key-A"
|
||||
);
|
||||
expect(wrapper.find(".focused").prop("id")).toBe("key-A");
|
||||
|
||||
wrapper.simulate("blur");
|
||||
expect(formatTree(wrapper)).toMatchSnapshot();
|
||||
expect(wrapper.getDOMNode().hasAttribute("aria-activedescendant")).toBe(
|
||||
false
|
||||
);
|
||||
expect(wrapper.find(".focused").exists()).toBe(false);
|
||||
});
|
||||
|
||||
it("renders as expected when navigating up with the keyboard", () => {
|
||||
const wrapper = mountTree({
|
||||
expanded: new Set("ABCDEFGHIJKLMNO".split("")),
|
||||
focused: "L"
|
||||
});
|
||||
expect(formatTree(wrapper)).toMatchSnapshot();
|
||||
expect(wrapper.getDOMNode().getAttribute("aria-activedescendant")).toBe(
|
||||
"key-L"
|
||||
);
|
||||
expect(wrapper.find(".focused").prop("id")).toBe("key-L");
|
||||
|
||||
simulateKeyDown(wrapper, "ArrowUp");
|
||||
expect(formatTree(wrapper)).toMatchSnapshot();
|
||||
expect(wrapper.getDOMNode().getAttribute("aria-activedescendant")).toBe(
|
||||
"key-K"
|
||||
);
|
||||
expect(wrapper.find(".focused").prop("id")).toBe("key-K");
|
||||
|
||||
simulateKeyDown(wrapper, "ArrowUp");
|
||||
expect(formatTree(wrapper)).toMatchSnapshot();
|
||||
expect(wrapper.getDOMNode().getAttribute("aria-activedescendant")).toBe(
|
||||
"key-E"
|
||||
);
|
||||
expect(wrapper.find(".focused").prop("id")).toBe("key-E");
|
||||
});
|
||||
|
||||
it("renders as expected navigating up with the keyboard on a root", () => {
|
||||
const wrapper = mountTree({
|
||||
expanded: new Set("ABCDEFGHIJKLMNO".split("")),
|
||||
focused: "A"
|
||||
});
|
||||
expect(formatTree(wrapper)).toMatchSnapshot();
|
||||
expect(wrapper.getDOMNode().getAttribute("aria-activedescendant")).toBe(
|
||||
"key-A"
|
||||
);
|
||||
expect(wrapper.find(".focused").prop("id")).toBe("key-A");
|
||||
|
||||
simulateKeyDown(wrapper, "ArrowUp");
|
||||
expect(formatTree(wrapper)).toMatchSnapshot();
|
||||
expect(wrapper.getDOMNode().getAttribute("aria-activedescendant")).toBe(
|
||||
"key-A"
|
||||
);
|
||||
expect(wrapper.find(".focused").prop("id")).toBe("key-A");
|
||||
});
|
||||
|
||||
it("renders as expected when navigating down with the keyboard", () => {
|
||||
const wrapper = mountTree({
|
||||
expanded: new Set("ABCDEFGHIJKLMNO".split("")),
|
||||
focused: "K"
|
||||
});
|
||||
expect(formatTree(wrapper)).toMatchSnapshot();
|
||||
expect(wrapper.getDOMNode().getAttribute("aria-activedescendant")).toBe(
|
||||
"key-K"
|
||||
);
|
||||
expect(wrapper.find(".focused").prop("id")).toBe("key-K");
|
||||
|
||||
simulateKeyDown(wrapper, "ArrowDown");
|
||||
expect(formatTree(wrapper)).toMatchSnapshot();
|
||||
expect(wrapper.getDOMNode().getAttribute("aria-activedescendant")).toBe(
|
||||
"key-L"
|
||||
);
|
||||
expect(wrapper.find(".focused").prop("id")).toBe("key-L");
|
||||
|
||||
simulateKeyDown(wrapper, "ArrowDown");
|
||||
expect(formatTree(wrapper)).toMatchSnapshot();
|
||||
expect(wrapper.getDOMNode().getAttribute("aria-activedescendant")).toBe(
|
||||
"key-F"
|
||||
);
|
||||
expect(wrapper.find(".focused").prop("id")).toBe("key-F");
|
||||
});
|
||||
|
||||
it("renders as expected navigating down with keyboard on last node", () => {
|
||||
const wrapper = mountTree({
|
||||
expanded: new Set("ABCDEFGHIJKLMNO".split("")),
|
||||
focused: "O"
|
||||
});
|
||||
expect(formatTree(wrapper)).toMatchSnapshot();
|
||||
expect(wrapper.getDOMNode().getAttribute("aria-activedescendant")).toBe(
|
||||
"key-O"
|
||||
);
|
||||
expect(wrapper.find(".focused").prop("id")).toBe("key-O");
|
||||
|
||||
simulateKeyDown(wrapper, "ArrowDown");
|
||||
expect(formatTree(wrapper)).toMatchSnapshot();
|
||||
expect(wrapper.getDOMNode().getAttribute("aria-activedescendant")).toBe(
|
||||
"key-O"
|
||||
);
|
||||
expect(wrapper.find(".focused").prop("id")).toBe("key-O");
|
||||
});
|
||||
|
||||
it("renders as expected when navigating with right/left arrows", () => {
|
||||
const wrapper = mountTree({
|
||||
expanded: new Set("ABCDEFGHIJKLMNO".split("")),
|
||||
focused: "L"
|
||||
});
|
||||
expect(formatTree(wrapper)).toMatchSnapshot();
|
||||
expect(wrapper.getDOMNode().getAttribute("aria-activedescendant")).toBe(
|
||||
"key-L"
|
||||
);
|
||||
expect(wrapper.find(".focused").prop("id")).toBe("key-L");
|
||||
|
||||
simulateKeyDown(wrapper, "ArrowLeft");
|
||||
expect(formatTree(wrapper)).toMatchSnapshot();
|
||||
expect(wrapper.getDOMNode().getAttribute("aria-activedescendant")).toBe(
|
||||
"key-E"
|
||||
);
|
||||
expect(wrapper.find(".focused").prop("id")).toBe("key-E");
|
||||
|
||||
simulateKeyDown(wrapper, "ArrowLeft");
|
||||
expect(formatTree(wrapper)).toMatchSnapshot();
|
||||
expect(wrapper.getDOMNode().getAttribute("aria-activedescendant")).toBe(
|
||||
"key-E"
|
||||
);
|
||||
expect(wrapper.find(".focused").prop("id")).toBe("key-E");
|
||||
|
||||
simulateKeyDown(wrapper, "ArrowRight");
|
||||
expect(formatTree(wrapper)).toMatchSnapshot();
|
||||
expect(wrapper.getDOMNode().getAttribute("aria-activedescendant")).toBe(
|
||||
"key-E"
|
||||
);
|
||||
expect(wrapper.find(".focused").prop("id")).toBe("key-E");
|
||||
|
||||
simulateKeyDown(wrapper, "ArrowRight");
|
||||
expect(formatTree(wrapper)).toMatchSnapshot();
|
||||
expect(wrapper.getDOMNode().getAttribute("aria-activedescendant")).toBe(
|
||||
"key-K"
|
||||
);
|
||||
expect(wrapper.find(".focused").prop("id")).toBe("key-K");
|
||||
});
|
||||
|
||||
it("renders as expected when navigating with left arrows on roots", () => {
|
||||
const wrapper = mountTree({
|
||||
focused: "M"
|
||||
});
|
||||
expect(formatTree(wrapper)).toMatchSnapshot();
|
||||
expect(wrapper.getDOMNode().getAttribute("aria-activedescendant")).toBe(
|
||||
"key-M"
|
||||
);
|
||||
expect(wrapper.find(".focused").prop("id")).toBe("key-M");
|
||||
|
||||
simulateKeyDown(wrapper, "ArrowLeft");
|
||||
expect(formatTree(wrapper)).toMatchSnapshot();
|
||||
expect(wrapper.getDOMNode().getAttribute("aria-activedescendant")).toBe(
|
||||
"key-A"
|
||||
);
|
||||
expect(wrapper.find(".focused").prop("id")).toBe("key-A");
|
||||
|
||||
simulateKeyDown(wrapper, "ArrowLeft");
|
||||
expect(formatTree(wrapper)).toMatchSnapshot();
|
||||
expect(wrapper.getDOMNode().getAttribute("aria-activedescendant")).toBe(
|
||||
"key-A"
|
||||
);
|
||||
expect(wrapper.find(".focused").prop("id")).toBe("key-A");
|
||||
});
|
||||
|
||||
it("renders as expected when navigating with home/end", () => {
|
||||
const wrapper = mountTree({
|
||||
focused: "M"
|
||||
});
|
||||
expect(formatTree(wrapper)).toMatchSnapshot();
|
||||
expect(wrapper.getDOMNode().getAttribute("aria-activedescendant")).toBe(
|
||||
"key-M"
|
||||
);
|
||||
expect(wrapper.find(".focused").prop("id")).toBe("key-M");
|
||||
|
||||
simulateKeyDown(wrapper, "Home");
|
||||
expect(formatTree(wrapper)).toMatchSnapshot();
|
||||
expect(wrapper.getDOMNode().getAttribute("aria-activedescendant")).toBe(
|
||||
"key-A"
|
||||
);
|
||||
expect(wrapper.find(".focused").prop("id")).toBe("key-A");
|
||||
|
||||
simulateKeyDown(wrapper, "Home");
|
||||
expect(formatTree(wrapper)).toMatchSnapshot();
|
||||
expect(wrapper.getDOMNode().getAttribute("aria-activedescendant")).toBe(
|
||||
"key-A"
|
||||
);
|
||||
expect(wrapper.find(".focused").prop("id")).toBe("key-A");
|
||||
|
||||
simulateKeyDown(wrapper, "End");
|
||||
expect(formatTree(wrapper)).toMatchSnapshot();
|
||||
expect(wrapper.getDOMNode().getAttribute("aria-activedescendant")).toBe(
|
||||
"key-M"
|
||||
);
|
||||
expect(wrapper.find(".focused").prop("id")).toBe("key-M");
|
||||
|
||||
simulateKeyDown(wrapper, "End");
|
||||
expect(formatTree(wrapper)).toMatchSnapshot();
|
||||
expect(wrapper.getDOMNode().getAttribute("aria-activedescendant")).toBe(
|
||||
"key-M"
|
||||
);
|
||||
expect(wrapper.find(".focused").prop("id")).toBe("key-M");
|
||||
|
||||
simulateKeyDown(wrapper, "ArrowRight");
|
||||
expect(formatTree(wrapper)).toMatchSnapshot();
|
||||
expect(wrapper.getDOMNode().getAttribute("aria-activedescendant")).toBe(
|
||||
"key-M"
|
||||
);
|
||||
expect(wrapper.find(".focused").prop("id")).toBe("key-M");
|
||||
|
||||
simulateKeyDown(wrapper, "End");
|
||||
expect(formatTree(wrapper)).toMatchSnapshot();
|
||||
expect(wrapper.getDOMNode().getAttribute("aria-activedescendant")).toBe(
|
||||
"key-N"
|
||||
);
|
||||
expect(wrapper.find(".focused").prop("id")).toBe("key-N");
|
||||
|
||||
simulateKeyDown(wrapper, "End");
|
||||
expect(formatTree(wrapper)).toMatchSnapshot();
|
||||
expect(wrapper.getDOMNode().getAttribute("aria-activedescendant")).toBe(
|
||||
"key-N"
|
||||
);
|
||||
expect(wrapper.find(".focused").prop("id")).toBe("key-N");
|
||||
|
||||
simulateKeyDown(wrapper, "Home");
|
||||
expect(formatTree(wrapper)).toMatchSnapshot();
|
||||
expect(wrapper.getDOMNode().getAttribute("aria-activedescendant")).toBe(
|
||||
"key-A"
|
||||
);
|
||||
expect(wrapper.find(".focused").prop("id")).toBe("key-A");
|
||||
});
|
||||
|
||||
it("renders as expected navigating with arrows on unexpandable roots", () => {
|
||||
const wrapper = mountTree({
|
||||
focused: "A",
|
||||
isExpandable: item => false
|
||||
});
|
||||
expect(formatTree(wrapper)).toMatchSnapshot();
|
||||
|
||||
simulateKeyDown(wrapper, "ArrowRight");
|
||||
expect(formatTree(wrapper)).toMatchSnapshot();
|
||||
expect(wrapper.getDOMNode().getAttribute("aria-activedescendant")).toBe(
|
||||
"key-M"
|
||||
);
|
||||
|
||||
simulateKeyDown(wrapper, "ArrowLeft");
|
||||
expect(formatTree(wrapper)).toMatchSnapshot();
|
||||
expect(wrapper.getDOMNode().getAttribute("aria-activedescendant")).toBe(
|
||||
"key-A"
|
||||
);
|
||||
});
|
||||
|
||||
it("calls onFocus when expected", () => {
|
||||
const onFocus = jest.fn(x => {
|
||||
wrapper &&
|
||||
wrapper.setState(() => {
|
||||
return { focused: x };
|
||||
});
|
||||
});
|
||||
|
||||
const wrapper = mountTree({
|
||||
expanded: new Set("ABCDEFGHIJKLMNO".split("")),
|
||||
focused: "I",
|
||||
onFocus
|
||||
});
|
||||
|
||||
simulateKeyDown(wrapper, "ArrowUp");
|
||||
expect(onFocus.mock.calls[0][0]).toBe("H");
|
||||
|
||||
simulateKeyDown(wrapper, "ArrowUp");
|
||||
expect(onFocus.mock.calls[1][0]).toBe("C");
|
||||
|
||||
simulateKeyDown(wrapper, "ArrowLeft");
|
||||
simulateKeyDown(wrapper, "ArrowLeft");
|
||||
expect(onFocus.mock.calls[2][0]).toBe("A");
|
||||
|
||||
simulateKeyDown(wrapper, "ArrowRight");
|
||||
expect(onFocus.mock.calls[3][0]).toBe("B");
|
||||
|
||||
simulateKeyDown(wrapper, "ArrowDown");
|
||||
expect(onFocus.mock.calls[4][0]).toBe("E");
|
||||
});
|
||||
|
||||
it("focus treeRef when a node is clicked", () => {
|
||||
const wrapper = mountTree({
|
||||
expanded: new Set("ABCDEFGHIJKLMNO".split(""))
|
||||
});
|
||||
const treeRef = wrapper
|
||||
.find("Tree")
|
||||
.first()
|
||||
.instance().treeRef;
|
||||
treeRef.focus = jest.fn();
|
||||
|
||||
getTreeNodes(wrapper)
|
||||
.first()
|
||||
.simulate("click");
|
||||
expect(treeRef.focus.mock.calls).toHaveLength(1);
|
||||
});
|
||||
|
||||
it("doesn't focus treeRef when focused is null", () => {
|
||||
const wrapper = mountTree({
|
||||
expanded: new Set("ABCDEFGHIJKLMNO".split("")),
|
||||
focused: "A"
|
||||
});
|
||||
const treeRef = wrapper
|
||||
.find("Tree")
|
||||
.first()
|
||||
.instance().treeRef;
|
||||
treeRef.focus = jest.fn();
|
||||
wrapper.simulate("blur");
|
||||
expect(treeRef.focus.mock.calls).toHaveLength(0);
|
||||
});
|
||||
|
||||
it("calls onActivate when expected", () => {
|
||||
const onActivate = jest.fn();
|
||||
|
||||
const wrapper = mountTree({
|
||||
expanded: new Set("ABCDEFGHIJKLMNO".split("")),
|
||||
focused: "A",
|
||||
onActivate
|
||||
});
|
||||
|
||||
simulateKeyDown(wrapper, "Enter");
|
||||
expect(onActivate.mock.calls).toHaveLength(1);
|
||||
expect(onActivate.mock.calls[0][0]).toBe("A");
|
||||
|
||||
simulateKeyDown(wrapper, "Enter");
|
||||
expect(onActivate.mock.calls).toHaveLength(2);
|
||||
expect(onActivate.mock.calls[1][0]).toBe("A");
|
||||
|
||||
simulateKeyDown(wrapper, "ArrowDown");
|
||||
simulateKeyDown(wrapper, "Enter");
|
||||
expect(onActivate.mock.calls).toHaveLength(3);
|
||||
expect(onActivate.mock.calls[2][0]).toBe("B");
|
||||
|
||||
wrapper.simulate("blur");
|
||||
simulateKeyDown(wrapper, "Enter");
|
||||
expect(onActivate.mock.calls).toHaveLength(3);
|
||||
});
|
||||
|
||||
it("does not throw when onActivate is undefined and Enter is pressed", () => {
|
||||
const wrapper = mountTree({
|
||||
expanded: new Set("ABCDEFGHIJKLMNO".split("")),
|
||||
focused: "A"
|
||||
});
|
||||
|
||||
simulateKeyDown(wrapper, "Enter");
|
||||
});
|
||||
|
||||
it("ignores key strokes when pressing modifiers", () => {
|
||||
const wrapper = mountTree({
|
||||
expanded: new Set("ABCDEFGHIJKLMNO".split("")),
|
||||
focused: "L"
|
||||
});
|
||||
expect(formatTree(wrapper)).toMatchSnapshot();
|
||||
expect(wrapper.getDOMNode().getAttribute("aria-activedescendant")).toBe(
|
||||
"key-L"
|
||||
);
|
||||
expect(wrapper.find(".focused").prop("id")).toBe("key-L");
|
||||
|
||||
const testKeys = [
|
||||
{ key: "ArrowDown" },
|
||||
{ key: "ArrowUp" },
|
||||
{ key: "ArrowLeft" },
|
||||
{ key: "ArrowRight" }
|
||||
];
|
||||
const modifiers = [
|
||||
{ altKey: true },
|
||||
{ ctrlKey: true },
|
||||
{ metaKey: true },
|
||||
{ shiftKey: true }
|
||||
];
|
||||
|
||||
for (const key of testKeys) {
|
||||
for (const modifier of modifiers) {
|
||||
wrapper.simulate("keydown", Object.assign({}, key, modifier));
|
||||
expect(formatTree(wrapper)).toMatchSnapshot();
|
||||
expect(wrapper.getDOMNode().getAttribute("aria-activedescendant")).toBe(
|
||||
"key-L"
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
it("renders arrows as expected when nodes are expanded", () => {
|
||||
const wrapper = mountTree({
|
||||
expanded: new Set("ABCDEFGHIJKLMNO".split(""))
|
||||
});
|
||||
expect(formatTree(wrapper)).toMatchSnapshot();
|
||||
|
||||
getTreeNodes(wrapper).forEach(n => {
|
||||
if ("ABECDMN".split("").includes(getSanitizedNodeText(n))) {
|
||||
expect(n.find(".arrow.expanded").exists()).toBe(true);
|
||||
} else {
|
||||
expect(n.find(".arrow").exists()).toBe(false);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it("renders arrows as expected when nodes are collapsed", () => {
|
||||
const wrapper = mountTree();
|
||||
expect(formatTree(wrapper)).toMatchSnapshot();
|
||||
|
||||
getTreeNodes(wrapper).forEach(n => {
|
||||
const arrow = n.find(".arrow");
|
||||
expect(arrow.exists()).toBe(true);
|
||||
expect(arrow.hasClass("expanded")).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
it("uses isExpandable prop if it exists to render tree nodes", () => {
|
||||
const wrapper = mountTree({
|
||||
isExpandable: item => item === "A"
|
||||
});
|
||||
expect(formatTree(wrapper)).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("adds the expected data-expandable attribute", () => {
|
||||
const wrapper = mountTree({
|
||||
isExpandable: item => item === "A"
|
||||
});
|
||||
const nodes = getTreeNodes(wrapper);
|
||||
expect(nodes.at(0).prop("data-expandable")).toBe(true);
|
||||
expect(nodes.at(1).prop("data-expandable")).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
function getTreeNodes(wrapper) {
|
||||
return wrapper.find(".tree-node");
|
||||
}
|
||||
|
||||
function simulateKeyDown(wrapper, key) {
|
||||
wrapper.simulate("keydown", {
|
||||
key,
|
||||
preventDefault: () => {},
|
||||
stopPropagation: () => {}
|
||||
});
|
||||
}
|
||||
|
||||
/*
|
||||
* Takes an Enzyme wrapper (obtained with mount/mount/…) and
|
||||
* returns a stringified version of the Tree, e.g.
|
||||
*
|
||||
* ▼ A
|
||||
* | ▼ B
|
||||
* | | ▼ E
|
||||
* | | | K
|
||||
* | | | L
|
||||
* | | F
|
||||
* | | G
|
||||
* | ▼ C
|
||||
* | | H
|
||||
* | | I
|
||||
* | ▼ D
|
||||
* | | J
|
||||
* ▼ M
|
||||
* | ▼ N
|
||||
* | | O
|
||||
*
|
||||
*/
|
||||
function formatTree(wrapper) {
|
||||
const textTree = getTreeNodes(wrapper)
|
||||
.map(node => {
|
||||
const level = (node.prop("aria-level") || 1) - 1;
|
||||
const indentStr = "| ".repeat(level);
|
||||
const arrow = node.find(".arrow");
|
||||
let arrowStr = " ";
|
||||
if (arrow.exists()) {
|
||||
arrowStr = arrow.hasClass("expanded") ? "▼ " : "▶︎ ";
|
||||
}
|
||||
|
||||
return `${indentStr}${arrowStr}${getSanitizedNodeText(node)}`;
|
||||
})
|
||||
.join("\n");
|
||||
|
||||
// Wrap in new lines so tree nodes are aligned as expected.
|
||||
return `\n${textTree}\n`;
|
||||
}
|
||||
|
||||
function getSanitizedNodeText(node) {
|
||||
// Stripping off the invisible space used in the indent.
|
||||
return node.text().replace(/^\u200B+/, "");
|
||||
}
|
||||
|
||||
// Encoding of the following tree/forest:
|
||||
//
|
||||
// A
|
||||
// |-- B
|
||||
// | |-- E
|
||||
// | | |-- K
|
||||
// | | `-- L
|
||||
// | |-- F
|
||||
// | `-- G
|
||||
// |-- C
|
||||
// | |-- H
|
||||
// | `-- I
|
||||
// `-- D
|
||||
// `-- J
|
||||
// M
|
||||
// `-- N
|
||||
// `-- O
|
||||
|
||||
var TEST_TREE = {
|
||||
children: {
|
||||
A: ["B", "C", "D"],
|
||||
B: ["E", "F", "G"],
|
||||
C: ["H", "I"],
|
||||
D: ["J"],
|
||||
E: ["K", "L"],
|
||||
F: [],
|
||||
G: [],
|
||||
H: [],
|
||||
I: [],
|
||||
J: [],
|
||||
K: [],
|
||||
L: [],
|
||||
M: ["N"],
|
||||
N: ["O"],
|
||||
O: []
|
||||
},
|
||||
parent: {
|
||||
A: null,
|
||||
B: "A",
|
||||
C: "A",
|
||||
D: "A",
|
||||
E: "B",
|
||||
F: "B",
|
||||
G: "B",
|
||||
H: "C",
|
||||
I: "C",
|
||||
J: "D",
|
||||
K: "E",
|
||||
L: "E",
|
||||
M: null,
|
||||
N: "M",
|
||||
O: "N"
|
||||
}
|
||||
};
|
|
@ -0,0 +1,90 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
/* We can remove the outline since we do add our own focus style on nodes */
|
||||
.tree:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.tree.inline {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.tree.nowrap {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.tree.noselect {
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
-o-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.tree .tree-node {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.tree .tree-node:not(.focused):hover {
|
||||
background-color: var(--theme-selection-background-hover);
|
||||
}
|
||||
|
||||
.tree-indent {
|
||||
display: inline-block;
|
||||
width: 12px;
|
||||
margin-inline-start: 5px;
|
||||
border-inline-start: 1px solid #A2D1FF;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
/* Align with expandables siblings (where we have the arrow) */
|
||||
.tree-node[data-expandable="false"] .tree-indent:last-of-type {
|
||||
margin-inline-end: 15px;
|
||||
}
|
||||
|
||||
/* For non expandable root nodes, we don't have .tree-indent elements, so we declare
|
||||
the margin on the start of the node */
|
||||
.tree-node[data-expandable="false"][aria-level="1"] {
|
||||
padding-inline-start: 15px
|
||||
}
|
||||
|
||||
.tree .tree-node[data-expandable="true"] {
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.tree-node button.arrow {
|
||||
background:url(/images/arrow.svg) no-repeat;
|
||||
background-size:contain;
|
||||
background-position:center center;
|
||||
width: 9px;
|
||||
height: 9px;
|
||||
border:0;
|
||||
padding:0;
|
||||
margin-inline-start: 1px;
|
||||
margin-inline-end: 4px;
|
||||
transform: rotate(-90deg);
|
||||
transform-origin: center center;
|
||||
transition: transform 0.125s ease;
|
||||
align-self: center;
|
||||
-moz-context-properties: fill;
|
||||
fill: var(--theme-splitter-color, #9B9B9B);
|
||||
}
|
||||
|
||||
html[dir="rtl"] .tree-node button.arrow {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
|
||||
.tree-node button.arrow.expanded.expanded {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
|
||||
.tree .tree-node.focused {
|
||||
color: white;
|
||||
background-color: var(--theme-selection-background, #0a84ff);
|
||||
}
|
||||
|
||||
.tree-node.focused button.arrow {
|
||||
fill: currentColor;
|
||||
}
|
|
@ -0,0 +1,852 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
|
||||
|
||||
import React from "react";
|
||||
const { Component, createFactory } = React;
|
||||
import dom from "react-dom-factories";
|
||||
import PropTypes from "prop-types";
|
||||
|
||||
require("./tree.css");
|
||||
|
||||
// depth
|
||||
const AUTO_EXPAND_DEPTH = 0;
|
||||
|
||||
/**
|
||||
* An arrow that displays whether its node is expanded (▼) or collapsed
|
||||
* (▶). When its node has no children, it is hidden.
|
||||
*/
|
||||
class ArrowExpander extends Component {
|
||||
static get propTypes() {
|
||||
return {
|
||||
expanded: PropTypes.bool
|
||||
};
|
||||
}
|
||||
|
||||
shouldComponentUpdate(nextProps, nextState) {
|
||||
return this.props.expanded !== nextProps.expanded;
|
||||
}
|
||||
|
||||
render() {
|
||||
const { expanded } = this.props;
|
||||
|
||||
const classNames = ["arrow"];
|
||||
if (expanded) {
|
||||
classNames.push("expanded");
|
||||
}
|
||||
return dom.button({
|
||||
className: classNames.join(" ")
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const treeIndent = dom.span({ className: "tree-indent" }, "\u200B");
|
||||
|
||||
class TreeNode extends Component {
|
||||
static get propTypes() {
|
||||
return {
|
||||
id: PropTypes.any.isRequired,
|
||||
index: PropTypes.number.isRequired,
|
||||
depth: PropTypes.number.isRequired,
|
||||
focused: PropTypes.bool.isRequired,
|
||||
expanded: PropTypes.bool.isRequired,
|
||||
item: PropTypes.any.isRequired,
|
||||
isExpandable: PropTypes.bool.isRequired,
|
||||
onClick: PropTypes.func,
|
||||
renderItem: PropTypes.func.isRequired
|
||||
};
|
||||
}
|
||||
|
||||
shouldComponentUpdate(nextProps) {
|
||||
return (
|
||||
this.props.item !== nextProps.item ||
|
||||
this.props.focused !== nextProps.focused ||
|
||||
this.props.expanded !== nextProps.expanded
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
depth,
|
||||
id,
|
||||
item,
|
||||
focused,
|
||||
expanded,
|
||||
renderItem,
|
||||
isExpandable
|
||||
} = this.props;
|
||||
|
||||
const arrow = isExpandable
|
||||
? ArrowExpanderFactory({
|
||||
item,
|
||||
expanded
|
||||
})
|
||||
: null;
|
||||
|
||||
let ariaExpanded;
|
||||
if (this.props.isExpandable) {
|
||||
ariaExpanded = false;
|
||||
}
|
||||
if (this.props.expanded) {
|
||||
ariaExpanded = true;
|
||||
}
|
||||
|
||||
const indents = Array.from({ length: depth }).fill(treeIndent);
|
||||
const items = indents.concat(
|
||||
renderItem(item, depth, focused, arrow, expanded)
|
||||
);
|
||||
|
||||
return dom.div(
|
||||
{
|
||||
id,
|
||||
className: `tree-node${focused ? " focused" : ""}`,
|
||||
onClick: this.props.onClick,
|
||||
role: "treeitem",
|
||||
"aria-level": depth + 1,
|
||||
"aria-expanded": ariaExpanded,
|
||||
"data-expandable": this.props.isExpandable
|
||||
},
|
||||
...items
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const ArrowExpanderFactory = createFactory(ArrowExpander);
|
||||
const TreeNodeFactory = createFactory(TreeNode);
|
||||
|
||||
/**
|
||||
* Create a function that calls the given function `fn` only once per animation
|
||||
* frame.
|
||||
*
|
||||
* @param {Function} fn
|
||||
* @returns {Function}
|
||||
*/
|
||||
function oncePerAnimationFrame(fn) {
|
||||
let animationId = null;
|
||||
let argsToPass = null;
|
||||
return function(...args) {
|
||||
argsToPass = args;
|
||||
if (animationId !== null) {
|
||||
return;
|
||||
}
|
||||
|
||||
animationId = requestAnimationFrame(() => {
|
||||
fn.call(this, ...argsToPass);
|
||||
animationId = null;
|
||||
argsToPass = null;
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* A generic tree component. See propTypes for the public API.
|
||||
*
|
||||
* This tree component doesn't make any assumptions about the structure of your
|
||||
* tree data. Whether children are computed on demand, or stored in an array in
|
||||
* the parent's `_children` property, it doesn't matter. We only require the
|
||||
* implementation of `getChildren`, `getRoots`, `getParent`, and `isExpanded`
|
||||
* functions.
|
||||
*
|
||||
* This tree component is well tested and reliable. See the tests in ./tests
|
||||
* and its usage in the performance and memory panels in mozilla-central.
|
||||
*
|
||||
* This tree component doesn't make any assumptions about how to render items in
|
||||
* the tree. You provide a `renderItem` function, and this component will ensure
|
||||
* that only those items whose parents are expanded and which are visible in the
|
||||
* viewport are rendered. The `renderItem` function could render the items as a
|
||||
* "traditional" tree or as rows in a table or anything else. It doesn't
|
||||
* restrict you to only one certain kind of tree.
|
||||
*
|
||||
* The tree comes with basic styling for the indent, the arrow, as well as
|
||||
* hovered and focused styles which can be override in CSS.
|
||||
*
|
||||
* ### Example Usage
|
||||
*
|
||||
* Suppose we have some tree data where each item has this form:
|
||||
*
|
||||
* {
|
||||
* id: Number,
|
||||
* label: String,
|
||||
* parent: Item or null,
|
||||
* children: Array of child items,
|
||||
* expanded: bool,
|
||||
* }
|
||||
*
|
||||
* Here is how we could render that data with this component:
|
||||
*
|
||||
* class MyTree extends Component {
|
||||
* static get propTypes() {
|
||||
* // The root item of the tree, with the form described above.
|
||||
* return {
|
||||
* root: PropTypes.object.isRequired
|
||||
* };
|
||||
* },
|
||||
*
|
||||
* render() {
|
||||
* return Tree({
|
||||
* itemHeight: 20, // px
|
||||
*
|
||||
* getRoots: () => [this.props.root],
|
||||
*
|
||||
* getParent: item => item.parent,
|
||||
* getChildren: item => item.children,
|
||||
* getKey: item => item.id,
|
||||
* isExpanded: item => item.expanded,
|
||||
*
|
||||
* renderItem: (item, depth, isFocused, arrow, isExpanded) => {
|
||||
* let className = "my-tree-item";
|
||||
* if (isFocused) {
|
||||
* className += " focused";
|
||||
* }
|
||||
* return dom.div({
|
||||
* className,
|
||||
* },
|
||||
* arrow,
|
||||
* // And here is the label for this item.
|
||||
* dom.span({ className: "my-tree-item-label" }, item.label)
|
||||
* );
|
||||
* },
|
||||
*
|
||||
* onExpand: item => dispatchExpandActionToRedux(item),
|
||||
* onCollapse: item => dispatchCollapseActionToRedux(item),
|
||||
* });
|
||||
* }
|
||||
* }
|
||||
*/
|
||||
class Tree extends Component {
|
||||
static get propTypes() {
|
||||
return {
|
||||
// Required props
|
||||
|
||||
// A function to get an item's parent, or null if it is a root.
|
||||
//
|
||||
// Type: getParent(item: Item) -> Maybe<Item>
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// // The parent of this item is stored in its `parent` property.
|
||||
// getParent: item => item.parent
|
||||
getParent: PropTypes.func.isRequired,
|
||||
|
||||
// A function to get an item's children.
|
||||
//
|
||||
// Type: getChildren(item: Item) -> [Item]
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// // This item's children are stored in its `children` property.
|
||||
// getChildren: item => item.children
|
||||
getChildren: PropTypes.func.isRequired,
|
||||
|
||||
// A function which takes an item and ArrowExpander component instance and
|
||||
// returns a component, or text, or anything else that React considers
|
||||
// renderable.
|
||||
//
|
||||
// Type: renderItem(item: Item,
|
||||
// depth: Number,
|
||||
// isFocused: Boolean,
|
||||
// arrow: ReactComponent,
|
||||
// isExpanded: Boolean) -> ReactRenderable
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// renderItem: (item, depth, isFocused, arrow, isExpanded) => {
|
||||
// let className = "my-tree-item";
|
||||
// if (isFocused) {
|
||||
// className += " focused";
|
||||
// }
|
||||
// return dom.div(
|
||||
// {
|
||||
// className,
|
||||
// style: { marginLeft: depth * 10 + "px" }
|
||||
// },
|
||||
// arrow,
|
||||
// dom.span({ className: "my-tree-item-label" }, item.label)
|
||||
// );
|
||||
// },
|
||||
renderItem: PropTypes.func.isRequired,
|
||||
|
||||
// A function which returns the roots of the tree (forest).
|
||||
//
|
||||
// Type: getRoots() -> [Item]
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// // In this case, we only have one top level, root item. You could
|
||||
// // return multiple items if you have many top level items in your
|
||||
// // tree.
|
||||
// getRoots: () => [this.props.rootOfMyTree]
|
||||
getRoots: PropTypes.func.isRequired,
|
||||
|
||||
// A function to get a unique key for the given item. This helps speed up
|
||||
// React's rendering a *TON*.
|
||||
//
|
||||
// Type: getKey(item: Item) -> String
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// getKey: item => `my-tree-item-${item.uniqueId}`
|
||||
getKey: PropTypes.func.isRequired,
|
||||
|
||||
// A function to get whether an item is expanded or not. If an item is not
|
||||
// expanded, then it must be collapsed.
|
||||
//
|
||||
// Type: isExpanded(item: Item) -> Boolean
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// isExpanded: item => item.expanded,
|
||||
isExpanded: PropTypes.func.isRequired,
|
||||
|
||||
// Optional props
|
||||
|
||||
// The currently focused item, if any such item exists.
|
||||
focused: PropTypes.any,
|
||||
|
||||
// Handle when a new item is focused.
|
||||
onFocus: PropTypes.func,
|
||||
|
||||
// The depth to which we should automatically expand new items.
|
||||
autoExpandDepth: PropTypes.number,
|
||||
// Should auto expand all new items or just the new items under the first
|
||||
// root item.
|
||||
autoExpandAll: PropTypes.bool,
|
||||
|
||||
// Auto expand a node only if number of its children
|
||||
// are less than autoExpandNodeChildrenLimit
|
||||
autoExpandNodeChildrenLimit: PropTypes.number,
|
||||
|
||||
// Note: the two properties below are mutually exclusive. Only one of the
|
||||
// label properties is necessary.
|
||||
// ID of an element whose textual content serves as an accessible label
|
||||
// for a tree.
|
||||
labelledby: PropTypes.string,
|
||||
// Accessibility label for a tree widget.
|
||||
label: PropTypes.string,
|
||||
|
||||
// Optional event handlers for when items are expanded or collapsed.
|
||||
// Useful for dispatching redux events and updating application state,
|
||||
// maybe lazily loading subtrees from a worker, etc.
|
||||
//
|
||||
// Type:
|
||||
// onExpand(item: Item)
|
||||
// onCollapse(item: Item)
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// onExpand: item => dispatchExpandActionToRedux(item)
|
||||
onExpand: PropTypes.func,
|
||||
onCollapse: PropTypes.func,
|
||||
// Optional event handler called with the current focused node when the
|
||||
// Enter key is pressed. Can be useful to allow further keyboard actions
|
||||
// within the tree node.
|
||||
onActivate: PropTypes.func,
|
||||
isExpandable: PropTypes.func,
|
||||
// Additional classes to add to the root element.
|
||||
className: PropTypes.string,
|
||||
// style object to be applied to the root element.
|
||||
style: PropTypes.object,
|
||||
// Prevents blur when Tree loses focus
|
||||
preventBlur: PropTypes.bool
|
||||
};
|
||||
}
|
||||
|
||||
static get defaultProps() {
|
||||
return {
|
||||
autoExpandDepth: AUTO_EXPAND_DEPTH,
|
||||
autoExpandAll: true
|
||||
};
|
||||
}
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
seen: new Set()
|
||||
};
|
||||
|
||||
this._onExpand = oncePerAnimationFrame(this._onExpand).bind(this);
|
||||
this._onCollapse = oncePerAnimationFrame(this._onCollapse).bind(this);
|
||||
this._focusPrevNode = oncePerAnimationFrame(this._focusPrevNode).bind(this);
|
||||
this._focusNextNode = oncePerAnimationFrame(this._focusNextNode).bind(this);
|
||||
this._focusParentNode = oncePerAnimationFrame(this._focusParentNode).bind(
|
||||
this
|
||||
);
|
||||
this._focusFirstNode = oncePerAnimationFrame(this._focusFirstNode).bind(
|
||||
this
|
||||
);
|
||||
this._focusLastNode = oncePerAnimationFrame(this._focusLastNode).bind(this);
|
||||
|
||||
this._autoExpand = this._autoExpand.bind(this);
|
||||
this._preventArrowKeyScrolling = this._preventArrowKeyScrolling.bind(this);
|
||||
this._dfs = this._dfs.bind(this);
|
||||
this._dfsFromRoots = this._dfsFromRoots.bind(this);
|
||||
this._focus = this._focus.bind(this);
|
||||
this._scrollNodeIntoView = this._scrollNodeIntoView.bind(this);
|
||||
this._onBlur = this._onBlur.bind(this);
|
||||
this._onKeyDown = this._onKeyDown.bind(this);
|
||||
this._nodeIsExpandable = this._nodeIsExpandable.bind(this);
|
||||
this._activateNode = oncePerAnimationFrame(this._activateNode).bind(this);
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this._autoExpand();
|
||||
if (this.props.focused) {
|
||||
this._scrollNodeIntoView(this.props.focused);
|
||||
// Always keep the focus on the tree itself.
|
||||
this.treeRef.focus();
|
||||
}
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
this._autoExpand();
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps, prevState) {
|
||||
if (this.props.focused && prevProps.focused !== this.props.focused) {
|
||||
this._scrollNodeIntoView(this.props.focused);
|
||||
// Always keep the focus on the tree itself.
|
||||
this.treeRef.focus();
|
||||
}
|
||||
}
|
||||
|
||||
_autoExpand() {
|
||||
const { autoExpandDepth, autoExpandNodeChildrenLimit } = this.props;
|
||||
if (!autoExpandDepth) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Automatically expand the first autoExpandDepth levels for new items. Do
|
||||
// not use the usual DFS infrastructure because we don't want to ignore
|
||||
// collapsed nodes.
|
||||
const autoExpand = (item, currentDepth) => {
|
||||
if (currentDepth >= autoExpandDepth || this.state.seen.has(item)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const children = this.props.getChildren(item);
|
||||
if (
|
||||
autoExpandNodeChildrenLimit &&
|
||||
children.length > autoExpandNodeChildrenLimit
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.props.onExpand(item);
|
||||
this.state.seen.add(item);
|
||||
|
||||
const length = children.length;
|
||||
for (let i = 0; i < length; i++) {
|
||||
autoExpand(children[i], currentDepth + 1);
|
||||
}
|
||||
};
|
||||
|
||||
const roots = this.props.getRoots();
|
||||
const length = roots.length;
|
||||
if (this.props.autoExpandAll) {
|
||||
for (let i = 0; i < length; i++) {
|
||||
autoExpand(roots[i], 0);
|
||||
}
|
||||
} else if (length != 0) {
|
||||
autoExpand(roots[0], 0);
|
||||
}
|
||||
}
|
||||
|
||||
_preventArrowKeyScrolling(e) {
|
||||
switch (e.key) {
|
||||
case "ArrowUp":
|
||||
case "ArrowDown":
|
||||
case "ArrowLeft":
|
||||
case "ArrowRight":
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
if (e.nativeEvent) {
|
||||
if (e.nativeEvent.preventDefault) {
|
||||
e.nativeEvent.preventDefault();
|
||||
}
|
||||
if (e.nativeEvent.stopPropagation) {
|
||||
e.nativeEvent.stopPropagation();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform a pre-order depth-first search from item.
|
||||
*/
|
||||
_dfs(item, maxDepth = Infinity, traversal = [], _depth = 0) {
|
||||
traversal.push({ item, depth: _depth });
|
||||
|
||||
if (!this.props.isExpanded(item)) {
|
||||
return traversal;
|
||||
}
|
||||
|
||||
const nextDepth = _depth + 1;
|
||||
|
||||
if (nextDepth > maxDepth) {
|
||||
return traversal;
|
||||
}
|
||||
|
||||
const children = this.props.getChildren(item);
|
||||
const length = children.length;
|
||||
for (let i = 0; i < length; i++) {
|
||||
this._dfs(children[i], maxDepth, traversal, nextDepth);
|
||||
}
|
||||
|
||||
return traversal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform a pre-order depth-first search over the whole forest.
|
||||
*/
|
||||
_dfsFromRoots(maxDepth = Infinity) {
|
||||
const traversal = [];
|
||||
|
||||
const roots = this.props.getRoots();
|
||||
const length = roots.length;
|
||||
for (let i = 0; i < length; i++) {
|
||||
this._dfs(roots[i], maxDepth, traversal);
|
||||
}
|
||||
|
||||
return traversal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Expands current row.
|
||||
*
|
||||
* @param {Object} item
|
||||
* @param {Boolean} expandAllChildren
|
||||
*/
|
||||
_onExpand(item, expandAllChildren) {
|
||||
if (this.props.onExpand) {
|
||||
this.props.onExpand(item);
|
||||
|
||||
if (expandAllChildren) {
|
||||
const children = this._dfs(item);
|
||||
const length = children.length;
|
||||
for (let i = 0; i < length; i++) {
|
||||
this.props.onExpand(children[i].item);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Collapses current row.
|
||||
*
|
||||
* @param {Object} item
|
||||
*/
|
||||
_onCollapse(item) {
|
||||
if (this.props.onCollapse) {
|
||||
this.props.onCollapse(item);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the passed in item to be the focused item.
|
||||
*
|
||||
* @param {Object|undefined} item
|
||||
* The item to be focused, or undefined to focus no item.
|
||||
*
|
||||
* @param {Object|undefined} options
|
||||
* An options object which can contain:
|
||||
* - dir: "up" or "down" to indicate if we should scroll the element
|
||||
* to the top or the bottom of the scrollable container when
|
||||
* the element is off canvas.
|
||||
*/
|
||||
_focus(item, options = {}) {
|
||||
const { preventAutoScroll } = options;
|
||||
if (item && !preventAutoScroll) {
|
||||
this._scrollNodeIntoView(item, options);
|
||||
}
|
||||
if (this.props.onFocus) {
|
||||
this.props.onFocus(item);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the passed in item to be the focused item.
|
||||
*
|
||||
* @param {Object|undefined} item
|
||||
* The item to be scrolled to.
|
||||
*
|
||||
* @param {Object|undefined} options
|
||||
* An options object which can contain:
|
||||
* - dir: "up" or "down" to indicate if we should scroll the element
|
||||
* to the top or the bottom of the scrollable container when
|
||||
* the element is off canvas.
|
||||
*/
|
||||
_scrollNodeIntoView(item, options = {}) {
|
||||
if (item !== undefined) {
|
||||
const treeElement = this.treeRef;
|
||||
const element = document.getElementById(this.props.getKey(item));
|
||||
|
||||
if (element) {
|
||||
const { top, bottom } = element.getBoundingClientRect();
|
||||
const closestScrolledParent = node => {
|
||||
if (node == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (node.scrollHeight > node.clientHeight) {
|
||||
return node;
|
||||
}
|
||||
return closestScrolledParent(node.parentNode);
|
||||
};
|
||||
const scrolledParent = closestScrolledParent(treeElement);
|
||||
const scrolledParentRect = scrolledParent
|
||||
? scrolledParent.getBoundingClientRect()
|
||||
: null;
|
||||
const isVisible =
|
||||
!scrolledParent ||
|
||||
(top >= scrolledParentRect.top &&
|
||||
bottom <= scrolledParentRect.bottom);
|
||||
|
||||
if (!isVisible) {
|
||||
const { alignTo } = options;
|
||||
const scrollToTop = alignTo
|
||||
? alignTo === "top"
|
||||
: !scrolledParentRect || top < scrolledParentRect.top;
|
||||
element.scrollIntoView(scrollToTop);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the state to have no focused item.
|
||||
*/
|
||||
_onBlur() {
|
||||
if (!this.props.preventBlur) {
|
||||
this._focus(undefined);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles key down events in the tree's container.
|
||||
*
|
||||
* @param {Event} e
|
||||
*/
|
||||
_onKeyDown(e) {
|
||||
if (this.props.focused == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Allow parent nodes to use navigation arrows with modifiers.
|
||||
if (e.altKey || e.ctrlKey || e.shiftKey || e.metaKey) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._preventArrowKeyScrolling(e);
|
||||
|
||||
switch (e.key) {
|
||||
case "ArrowUp":
|
||||
this._focusPrevNode();
|
||||
return;
|
||||
|
||||
case "ArrowDown":
|
||||
this._focusNextNode();
|
||||
return;
|
||||
|
||||
case "ArrowLeft":
|
||||
if (
|
||||
this.props.isExpanded(this.props.focused) &&
|
||||
this._nodeIsExpandable(this.props.focused)
|
||||
) {
|
||||
this._onCollapse(this.props.focused);
|
||||
} else {
|
||||
this._focusParentNode();
|
||||
}
|
||||
return;
|
||||
|
||||
case "ArrowRight":
|
||||
if (
|
||||
this._nodeIsExpandable(this.props.focused) &&
|
||||
!this.props.isExpanded(this.props.focused)
|
||||
) {
|
||||
this._onExpand(this.props.focused);
|
||||
} else {
|
||||
this._focusNextNode();
|
||||
}
|
||||
return;
|
||||
|
||||
case "Home":
|
||||
this._focusFirstNode();
|
||||
return;
|
||||
|
||||
case "End":
|
||||
this._focusLastNode();
|
||||
return;
|
||||
|
||||
case "Enter":
|
||||
this._activateNode();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the previous node relative to the currently focused item, to focused.
|
||||
*/
|
||||
_focusPrevNode() {
|
||||
// Start a depth first search and keep going until we reach the currently
|
||||
// focused node. Focus the previous node in the DFS, if it exists. If it
|
||||
// doesn't exist, we're at the first node already.
|
||||
|
||||
let prev;
|
||||
|
||||
const traversal = this._dfsFromRoots();
|
||||
const length = traversal.length;
|
||||
for (let i = 0; i < length; i++) {
|
||||
const item = traversal[i].item;
|
||||
if (item === this.props.focused) {
|
||||
break;
|
||||
}
|
||||
prev = item;
|
||||
}
|
||||
if (prev === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._focus(prev, { alignTo: "top" });
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the down arrow key which will focus either the next child
|
||||
* or sibling row.
|
||||
*/
|
||||
_focusNextNode() {
|
||||
// Start a depth first search and keep going until we reach the currently
|
||||
// focused node. Focus the next node in the DFS, if it exists. If it
|
||||
// doesn't exist, we're at the last node already.
|
||||
const traversal = this._dfsFromRoots();
|
||||
const length = traversal.length;
|
||||
let i = 0;
|
||||
|
||||
while (i < length) {
|
||||
if (traversal[i].item === this.props.focused) {
|
||||
break;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
if (i + 1 < traversal.length) {
|
||||
this._focus(traversal[i + 1].item, { alignTo: "bottom" });
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the left arrow key, going back up to the current rows'
|
||||
* parent row.
|
||||
*/
|
||||
_focusParentNode() {
|
||||
const parent = this.props.getParent(this.props.focused);
|
||||
if (!parent) {
|
||||
this._focusPrevNode(this.props.focused);
|
||||
return;
|
||||
}
|
||||
|
||||
this._focus(parent, { alignTo: "top" });
|
||||
}
|
||||
|
||||
_focusFirstNode() {
|
||||
const traversal = this._dfsFromRoots();
|
||||
this._focus(traversal[0].item, { alignTo: "top" });
|
||||
}
|
||||
|
||||
_focusLastNode() {
|
||||
const traversal = this._dfsFromRoots();
|
||||
const lastIndex = traversal.length - 1;
|
||||
this._focus(traversal[lastIndex].item, { alignTo: "bottom" });
|
||||
}
|
||||
|
||||
_activateNode() {
|
||||
if (this.props.onActivate) {
|
||||
this.props.onActivate(this.props.focused);
|
||||
}
|
||||
}
|
||||
|
||||
_nodeIsExpandable(item) {
|
||||
return this.props.isExpandable
|
||||
? this.props.isExpandable(item)
|
||||
: !!this.props.getChildren(item).length;
|
||||
}
|
||||
|
||||
render() {
|
||||
const traversal = this._dfsFromRoots();
|
||||
const { focused } = this.props;
|
||||
|
||||
const nodes = traversal.map((v, i) => {
|
||||
const { item, depth } = traversal[i];
|
||||
const key = this.props.getKey(item, i);
|
||||
return TreeNodeFactory({
|
||||
key,
|
||||
id: key,
|
||||
index: i,
|
||||
item,
|
||||
depth,
|
||||
renderItem: this.props.renderItem,
|
||||
focused: focused === item,
|
||||
expanded: this.props.isExpanded(item),
|
||||
isExpandable: this._nodeIsExpandable(item),
|
||||
onExpand: this._onExpand,
|
||||
onCollapse: this._onCollapse,
|
||||
onClick: e => {
|
||||
// We can stop the propagation since click handler on the node can be
|
||||
// created in `renderItem`.
|
||||
e.stopPropagation();
|
||||
|
||||
// Since the user just clicked the node, there's no need to check if
|
||||
// it should be scrolled into view.
|
||||
this._focus(item, { preventAutoScroll: true });
|
||||
if (this.props.isExpanded(item)) {
|
||||
this.props.onCollapse(item);
|
||||
} else {
|
||||
this.props.onExpand(item, e.altKey);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
const style = Object.assign({}, this.props.style || {}, {
|
||||
padding: 0,
|
||||
margin: 0
|
||||
});
|
||||
|
||||
return dom.div(
|
||||
{
|
||||
className: `tree ${this.props.className ? this.props.className : ""}`,
|
||||
ref: el => {
|
||||
this.treeRef = el;
|
||||
},
|
||||
role: "tree",
|
||||
tabIndex: "0",
|
||||
onKeyDown: this._onKeyDown,
|
||||
onKeyPress: this._preventArrowKeyScrolling,
|
||||
onKeyUp: this._preventArrowKeyScrolling,
|
||||
onFocus: ({ nativeEvent }) => {
|
||||
if (focused || !nativeEvent || !this.treeRef) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { explicitOriginalTarget } = nativeEvent;
|
||||
// Only set default focus to the first tree node if the focus came
|
||||
// from outside the tree (e.g. by tabbing to the tree from other
|
||||
// external elements).
|
||||
if (
|
||||
explicitOriginalTarget !== this.treeRef &&
|
||||
!this.treeRef.contains(explicitOriginalTarget)
|
||||
) {
|
||||
this._focus(traversal[0].item);
|
||||
}
|
||||
},
|
||||
onBlur: this._onBlur,
|
||||
"aria-label": this.props.label,
|
||||
"aria-labelledby": this.props.labelledby,
|
||||
"aria-activedescendant": focused && this.props.getKey(focused),
|
||||
style
|
||||
},
|
||||
nodes
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default Tree;
|
|
@ -0,0 +1,5 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
require("./tree");
|
|
@ -0,0 +1,244 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
import React from "react";
|
||||
const { Component, createFactory, createElement } = React;
|
||||
|
||||
import Components from "../index";
|
||||
const Tree = createFactory(Components.Tree);
|
||||
import { storiesOf } from "@storybook/react";
|
||||
|
||||
storiesOf("Tree", module)
|
||||
.add("Simple tree - autoExpand 1", () => {
|
||||
return renderTree({
|
||||
autoExpandDepth: 1,
|
||||
});
|
||||
})
|
||||
.add("Simple tree - autoExpand 2", () => {
|
||||
return renderTree({
|
||||
autoExpandDepth: 2,
|
||||
});
|
||||
})
|
||||
.add("Simple tree - autoExpand ∞", () => {
|
||||
return renderTree({
|
||||
autoExpandDepth: Infinity,
|
||||
});
|
||||
})
|
||||
.add("Multiple root tree", () => {
|
||||
return renderTree({
|
||||
autoExpandDepth: Infinity,
|
||||
getRoots: () => ["A", "P", "M", "Q", "W", "R"],
|
||||
});
|
||||
})
|
||||
.add("focused node", () => {
|
||||
return renderTree({
|
||||
focused: "W",
|
||||
getRoots: () => ["A", "W"],
|
||||
expanded: new Set(["A"])
|
||||
});
|
||||
})
|
||||
.add("variable height nodes", () => {
|
||||
const nodes = Array.from({length: 10})
|
||||
.map((_, i) => `item ${i + 1} - `.repeat(10 + Math.random() * 50));
|
||||
return renderTree({
|
||||
getRoots: () => ["ROOT"],
|
||||
expanded: new Set(["ROOT"])
|
||||
}, {
|
||||
children: {"ROOT": nodes}
|
||||
});
|
||||
})
|
||||
.add("scrollable tree", () => {
|
||||
const nodes = Array.from({length: 500}).map((_, i) => (i + 1).toString());
|
||||
|
||||
class container extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
const state = {
|
||||
expanded: new Set(),
|
||||
focused: null
|
||||
};
|
||||
this.state = state;
|
||||
}
|
||||
|
||||
render() {
|
||||
return createElement("div", {},
|
||||
createElement("label", {
|
||||
style: {position: "fixed", right: 0},
|
||||
},
|
||||
"Enter node number to set focus on: ",
|
||||
createElement("input", {
|
||||
type: "number",
|
||||
min: 1,
|
||||
max: 500,
|
||||
onInput: e => {
|
||||
// Storing balue since `e` can be reused by the time the setState
|
||||
// callback is called.
|
||||
const value = e.target.value.toString();
|
||||
this.setState(previousState => {
|
||||
return {focused: value};
|
||||
});
|
||||
}
|
||||
}),
|
||||
),
|
||||
createTreeElement({getRoots: () => nodes}, this, {})
|
||||
);
|
||||
}
|
||||
}
|
||||
return createFactory(container)();
|
||||
})
|
||||
.add("scrollable tree with focused node", () => {
|
||||
const nodes = Array.from({length: 500}).map((_, i) => `item ${i + 1}`);
|
||||
return renderTree({
|
||||
focused: "item 250",
|
||||
getRoots: () => nodes,
|
||||
}, {});
|
||||
})
|
||||
.add("1000 items tree", () => {
|
||||
const nodes = Array.from({length: 1000}).map((_, i) => `item-${i + 1}`);
|
||||
return renderTree({
|
||||
getRoots: () => ["ROOT"],
|
||||
expanded: new Set()
|
||||
}, {
|
||||
children: {"ROOT": nodes}
|
||||
});
|
||||
})
|
||||
.add("30,000 items tree", () => {
|
||||
const nodes = Array.from({length: 1000}).map((_, i) => `item-${i + 1}`);
|
||||
return renderTree({
|
||||
getRoots: () => nodes,
|
||||
expanded: new Set(Array.from({length: 2000}).map((_, i) => `item-${i + 1}`))
|
||||
}, {
|
||||
children: Array.from({length: 2000}).reduce((res, _, i) => {
|
||||
res[`item-${i + 1}`] = [`item-${i + 1001}`];
|
||||
return res;
|
||||
}, {})
|
||||
});
|
||||
});
|
||||
|
||||
// Encoding of the following tree/forest:
|
||||
//
|
||||
// A
|
||||
// |-- B
|
||||
// | |-- E
|
||||
// | | |-- K
|
||||
// | | `-- L
|
||||
// | |-- F
|
||||
// | `-- G
|
||||
// |-- C
|
||||
// | |-- H
|
||||
// | `-- I
|
||||
// `-- D
|
||||
// `-- J
|
||||
// M
|
||||
// `-- N
|
||||
// `-- O
|
||||
// P
|
||||
// Q
|
||||
// R
|
||||
// W
|
||||
// `-- X
|
||||
// `-- Z
|
||||
// `-- Y
|
||||
const TEST_TREE = {
|
||||
children: {
|
||||
A: ["B", "C", "D"],
|
||||
B: ["E", "F", "G"],
|
||||
C: ["H", "I"],
|
||||
D: ["J"],
|
||||
E: ["K", "L"],
|
||||
F: [],
|
||||
G: [],
|
||||
H: [],
|
||||
I: [],
|
||||
J: [],
|
||||
K: [],
|
||||
L: [],
|
||||
M: ["N"],
|
||||
N: ["O"],
|
||||
O: [],
|
||||
P: [],
|
||||
Q: [],
|
||||
R: [],
|
||||
W: ["X", "Y"],
|
||||
X: ["Z"],
|
||||
Y: [],
|
||||
Z: [],
|
||||
},
|
||||
parent: {
|
||||
A: null,
|
||||
B: "A",
|
||||
C: "A",
|
||||
D: "A",
|
||||
E: "B",
|
||||
F: "B",
|
||||
G: "B",
|
||||
H: "C",
|
||||
I: "C",
|
||||
J: "D",
|
||||
K: "E",
|
||||
L: "E",
|
||||
M: null,
|
||||
N: "M",
|
||||
O: "N",
|
||||
P: null,
|
||||
Q: null,
|
||||
R: null,
|
||||
W: null,
|
||||
X: "W",
|
||||
Y: "W",
|
||||
Z: "X",
|
||||
},
|
||||
};
|
||||
|
||||
function renderTree(props, tree = TEST_TREE) {
|
||||
class container extends Component {
|
||||
constructor(props2) {
|
||||
super(props2);
|
||||
const state = {
|
||||
expanded: props2.expanded || new Set(),
|
||||
focused: props2.focused
|
||||
};
|
||||
delete props2.focused;
|
||||
this.state = state;
|
||||
}
|
||||
|
||||
render() {
|
||||
return createTreeElement(props, this, tree);
|
||||
}
|
||||
}
|
||||
return createFactory(container)();
|
||||
}
|
||||
|
||||
function createTreeElement(props, context, tree) {
|
||||
return Tree(Object.assign({
|
||||
getParent: x => tree.parent && tree.parent[x],
|
||||
getChildren: x => tree.children && tree.children[x]
|
||||
? tree.children[x]
|
||||
: [],
|
||||
renderItem: (x, depth, focused, arrow, expanded) => [arrow, x],
|
||||
getRoots: () => ["A"],
|
||||
getKey: x => "key-" + x,
|
||||
onFocus: x => {
|
||||
context.setState(previousState => {
|
||||
return {focused: x};
|
||||
});
|
||||
},
|
||||
onExpand: x => {
|
||||
context.setState(previousState => {
|
||||
const expanded = new Set(previousState.expanded);
|
||||
expanded.add(x);
|
||||
return {expanded};
|
||||
});
|
||||
},
|
||||
onCollapse: x => {
|
||||
context.setState(previousState => {
|
||||
const expanded = new Set(previousState.expanded);
|
||||
expanded.delete(x);
|
||||
return {expanded};
|
||||
});
|
||||
},
|
||||
isExpanded: x => context.state && context.state.expanded.has(x),
|
||||
focused: context.state.focused,
|
||||
}, props));
|
||||
}
|
|
@ -0,0 +1,373 @@
|
|||
Mozilla Public License Version 2.0
|
||||
==================================
|
||||
|
||||
1. Definitions
|
||||
--------------
|
||||
|
||||
1.1. "Contributor"
|
||||
means each individual or legal entity that creates, contributes to
|
||||
the creation of, or owns Covered Software.
|
||||
|
||||
1.2. "Contributor Version"
|
||||
means the combination of the Contributions of others (if any) used
|
||||
by a Contributor and that particular Contributor's Contribution.
|
||||
|
||||
1.3. "Contribution"
|
||||
means Covered Software of a particular Contributor.
|
||||
|
||||
1.4. "Covered Software"
|
||||
means Source Code Form to which the initial Contributor has attached
|
||||
the notice in Exhibit A, the Executable Form of such Source Code
|
||||
Form, and Modifications of such Source Code Form, in each case
|
||||
including portions thereof.
|
||||
|
||||
1.5. "Incompatible With Secondary Licenses"
|
||||
means
|
||||
|
||||
(a) that the initial Contributor has attached the notice described
|
||||
in Exhibit B to the Covered Software; or
|
||||
|
||||
(b) that the Covered Software was made available under the terms of
|
||||
version 1.1 or earlier of the License, but not also under the
|
||||
terms of a Secondary License.
|
||||
|
||||
1.6. "Executable Form"
|
||||
means any form of the work other than Source Code Form.
|
||||
|
||||
1.7. "Larger Work"
|
||||
means a work that combines Covered Software with other material, in
|
||||
a separate file or files, that is not Covered Software.
|
||||
|
||||
1.8. "License"
|
||||
means this document.
|
||||
|
||||
1.9. "Licensable"
|
||||
means having the right to grant, to the maximum extent possible,
|
||||
whether at the time of the initial grant or subsequently, any and
|
||||
all of the rights conveyed by this License.
|
||||
|
||||
1.10. "Modifications"
|
||||
means any of the following:
|
||||
|
||||
(a) any file in Source Code Form that results from an addition to,
|
||||
deletion from, or modification of the contents of Covered
|
||||
Software; or
|
||||
|
||||
(b) any new file in Source Code Form that contains any Covered
|
||||
Software.
|
||||
|
||||
1.11. "Patent Claims" of a Contributor
|
||||
means any patent claim(s), including without limitation, method,
|
||||
process, and apparatus claims, in any patent Licensable by such
|
||||
Contributor that would be infringed, but for the grant of the
|
||||
License, by the making, using, selling, offering for sale, having
|
||||
made, import, or transfer of either its Contributions or its
|
||||
Contributor Version.
|
||||
|
||||
1.12. "Secondary License"
|
||||
means either the GNU General Public License, Version 2.0, the GNU
|
||||
Lesser General Public License, Version 2.1, the GNU Affero General
|
||||
Public License, Version 3.0, or any later versions of those
|
||||
licenses.
|
||||
|
||||
1.13. "Source Code Form"
|
||||
means the form of the work preferred for making modifications.
|
||||
|
||||
1.14. "You" (or "Your")
|
||||
means an individual or a legal entity exercising rights under this
|
||||
License. For legal entities, "You" includes any entity that
|
||||
controls, is controlled by, or is under common control with You. For
|
||||
purposes of this definition, "control" means (a) the power, direct
|
||||
or indirect, to cause the direction or management of such entity,
|
||||
whether by contract or otherwise, or (b) ownership of more than
|
||||
fifty percent (50%) of the outstanding shares or beneficial
|
||||
ownership of such entity.
|
||||
|
||||
2. License Grants and Conditions
|
||||
--------------------------------
|
||||
|
||||
2.1. Grants
|
||||
|
||||
Each Contributor hereby grants You a world-wide, royalty-free,
|
||||
non-exclusive license:
|
||||
|
||||
(a) under intellectual property rights (other than patent or trademark)
|
||||
Licensable by such Contributor to use, reproduce, make available,
|
||||
modify, display, perform, distribute, and otherwise exploit its
|
||||
Contributions, either on an unmodified basis, with Modifications, or
|
||||
as part of a Larger Work; and
|
||||
|
||||
(b) under Patent Claims of such Contributor to make, use, sell, offer
|
||||
for sale, have made, import, and otherwise transfer either its
|
||||
Contributions or its Contributor Version.
|
||||
|
||||
2.2. Effective Date
|
||||
|
||||
The licenses granted in Section 2.1 with respect to any Contribution
|
||||
become effective for each Contribution on the date the Contributor first
|
||||
distributes such Contribution.
|
||||
|
||||
2.3. Limitations on Grant Scope
|
||||
|
||||
The licenses granted in this Section 2 are the only rights granted under
|
||||
this License. No additional rights or licenses will be implied from the
|
||||
distribution or licensing of Covered Software under this License.
|
||||
Notwithstanding Section 2.1(b) above, no patent license is granted by a
|
||||
Contributor:
|
||||
|
||||
(a) for any code that a Contributor has removed from Covered Software;
|
||||
or
|
||||
|
||||
(b) for infringements caused by: (i) Your and any other third party's
|
||||
modifications of Covered Software, or (ii) the combination of its
|
||||
Contributions with other software (except as part of its Contributor
|
||||
Version); or
|
||||
|
||||
(c) under Patent Claims infringed by Covered Software in the absence of
|
||||
its Contributions.
|
||||
|
||||
This License does not grant any rights in the trademarks, service marks,
|
||||
or logos of any Contributor (except as may be necessary to comply with
|
||||
the notice requirements in Section 3.4).
|
||||
|
||||
2.4. Subsequent Licenses
|
||||
|
||||
No Contributor makes additional grants as a result of Your choice to
|
||||
distribute the Covered Software under a subsequent version of this
|
||||
License (see Section 10.2) or under the terms of a Secondary License (if
|
||||
permitted under the terms of Section 3.3).
|
||||
|
||||
2.5. Representation
|
||||
|
||||
Each Contributor represents that the Contributor believes its
|
||||
Contributions are its original creation(s) or it has sufficient rights
|
||||
to grant the rights to its Contributions conveyed by this License.
|
||||
|
||||
2.6. Fair Use
|
||||
|
||||
This License is not intended to limit any rights You have under
|
||||
applicable copyright doctrines of fair use, fair dealing, or other
|
||||
equivalents.
|
||||
|
||||
2.7. Conditions
|
||||
|
||||
Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
|
||||
in Section 2.1.
|
||||
|
||||
3. Responsibilities
|
||||
-------------------
|
||||
|
||||
3.1. Distribution of Source Form
|
||||
|
||||
All distribution of Covered Software in Source Code Form, including any
|
||||
Modifications that You create or to which You contribute, must be under
|
||||
the terms of this License. You must inform recipients that the Source
|
||||
Code Form of the Covered Software is governed by the terms of this
|
||||
License, and how they can obtain a copy of this License. You may not
|
||||
attempt to alter or restrict the recipients' rights in the Source Code
|
||||
Form.
|
||||
|
||||
3.2. Distribution of Executable Form
|
||||
|
||||
If You distribute Covered Software in Executable Form then:
|
||||
|
||||
(a) such Covered Software must also be made available in Source Code
|
||||
Form, as described in Section 3.1, and You must inform recipients of
|
||||
the Executable Form how they can obtain a copy of such Source Code
|
||||
Form by reasonable means in a timely manner, at a charge no more
|
||||
than the cost of distribution to the recipient; and
|
||||
|
||||
(b) You may distribute such Executable Form under the terms of this
|
||||
License, or sublicense it under different terms, provided that the
|
||||
license for the Executable Form does not attempt to limit or alter
|
||||
the recipients' rights in the Source Code Form under this License.
|
||||
|
||||
3.3. Distribution of a Larger Work
|
||||
|
||||
You may create and distribute a Larger Work under terms of Your choice,
|
||||
provided that You also comply with the requirements of this License for
|
||||
the Covered Software. If the Larger Work is a combination of Covered
|
||||
Software with a work governed by one or more Secondary Licenses, and the
|
||||
Covered Software is not Incompatible With Secondary Licenses, this
|
||||
License permits You to additionally distribute such Covered Software
|
||||
under the terms of such Secondary License(s), so that the recipient of
|
||||
the Larger Work may, at their option, further distribute the Covered
|
||||
Software under the terms of either this License or such Secondary
|
||||
License(s).
|
||||
|
||||
3.4. Notices
|
||||
|
||||
You may not remove or alter the substance of any license notices
|
||||
(including copyright notices, patent notices, disclaimers of warranty,
|
||||
or limitations of liability) contained within the Source Code Form of
|
||||
the Covered Software, except that You may alter any license notices to
|
||||
the extent required to remedy known factual inaccuracies.
|
||||
|
||||
3.5. Application of Additional Terms
|
||||
|
||||
You may choose to offer, and to charge a fee for, warranty, support,
|
||||
indemnity or liability obligations to one or more recipients of Covered
|
||||
Software. However, You may do so only on Your own behalf, and not on
|
||||
behalf of any Contributor. You must make it absolutely clear that any
|
||||
such warranty, support, indemnity, or liability obligation is offered by
|
||||
You alone, and You hereby agree to indemnify every Contributor for any
|
||||
liability incurred by such Contributor as a result of warranty, support,
|
||||
indemnity or liability terms You offer. You may include additional
|
||||
disclaimers of warranty and limitations of liability specific to any
|
||||
jurisdiction.
|
||||
|
||||
4. Inability to Comply Due to Statute or Regulation
|
||||
---------------------------------------------------
|
||||
|
||||
If it is impossible for You to comply with any of the terms of this
|
||||
License with respect to some or all of the Covered Software due to
|
||||
statute, judicial order, or regulation then You must: (a) comply with
|
||||
the terms of this License to the maximum extent possible; and (b)
|
||||
describe the limitations and the code they affect. Such description must
|
||||
be placed in a text file included with all distributions of the Covered
|
||||
Software under this License. Except to the extent prohibited by statute
|
||||
or regulation, such description must be sufficiently detailed for a
|
||||
recipient of ordinary skill to be able to understand it.
|
||||
|
||||
5. Termination
|
||||
--------------
|
||||
|
||||
5.1. The rights granted under this License will terminate automatically
|
||||
if You fail to comply with any of its terms. However, if You become
|
||||
compliant, then the rights granted under this License from a particular
|
||||
Contributor are reinstated (a) provisionally, unless and until such
|
||||
Contributor explicitly and finally terminates Your grants, and (b) on an
|
||||
ongoing basis, if such Contributor fails to notify You of the
|
||||
non-compliance by some reasonable means prior to 60 days after You have
|
||||
come back into compliance. Moreover, Your grants from a particular
|
||||
Contributor are reinstated on an ongoing basis if such Contributor
|
||||
notifies You of the non-compliance by some reasonable means, this is the
|
||||
first time You have received notice of non-compliance with this License
|
||||
from such Contributor, and You become compliant prior to 30 days after
|
||||
Your receipt of the notice.
|
||||
|
||||
5.2. If You initiate litigation against any entity by asserting a patent
|
||||
infringement claim (excluding declaratory judgment actions,
|
||||
counter-claims, and cross-claims) alleging that a Contributor Version
|
||||
directly or indirectly infringes any patent, then the rights granted to
|
||||
You by any and all Contributors for the Covered Software under Section
|
||||
2.1 of this License shall terminate.
|
||||
|
||||
5.3. In the event of termination under Sections 5.1 or 5.2 above, all
|
||||
end user license agreements (excluding distributors and resellers) which
|
||||
have been validly granted by You or Your distributors under this License
|
||||
prior to termination shall survive termination.
|
||||
|
||||
************************************************************************
|
||||
* *
|
||||
* 6. Disclaimer of Warranty *
|
||||
* ------------------------- *
|
||||
* *
|
||||
* Covered Software is provided under this License on an "as is" *
|
||||
* basis, without warranty of any kind, either expressed, implied, or *
|
||||
* statutory, including, without limitation, warranties that the *
|
||||
* Covered Software is free of defects, merchantable, fit for a *
|
||||
* particular purpose or non-infringing. The entire risk as to the *
|
||||
* quality and performance of the Covered Software is with You. *
|
||||
* Should any Covered Software prove defective in any respect, You *
|
||||
* (not any Contributor) assume the cost of any necessary servicing, *
|
||||
* repair, or correction. This disclaimer of warranty constitutes an *
|
||||
* essential part of this License. No use of any Covered Software is *
|
||||
* authorized under this License except under this disclaimer. *
|
||||
* *
|
||||
************************************************************************
|
||||
|
||||
************************************************************************
|
||||
* *
|
||||
* 7. Limitation of Liability *
|
||||
* -------------------------- *
|
||||
* *
|
||||
* Under no circumstances and under no legal theory, whether tort *
|
||||
* (including negligence), contract, or otherwise, shall any *
|
||||
* Contributor, or anyone who distributes Covered Software as *
|
||||
* permitted above, be liable to You for any direct, indirect, *
|
||||
* special, incidental, or consequential damages of any character *
|
||||
* including, without limitation, damages for lost profits, loss of *
|
||||
* goodwill, work stoppage, computer failure or malfunction, or any *
|
||||
* and all other commercial damages or losses, even if such party *
|
||||
* shall have been informed of the possibility of such damages. This *
|
||||
* limitation of liability shall not apply to liability for death or *
|
||||
* personal injury resulting from such party's negligence to the *
|
||||
* extent applicable law prohibits such limitation. Some *
|
||||
* jurisdictions do not allow the exclusion or limitation of *
|
||||
* incidental or consequential damages, so this exclusion and *
|
||||
* limitation may not apply to You. *
|
||||
* *
|
||||
************************************************************************
|
||||
|
||||
8. Litigation
|
||||
-------------
|
||||
|
||||
Any litigation relating to this License may be brought only in the
|
||||
courts of a jurisdiction where the defendant maintains its principal
|
||||
place of business and such litigation shall be governed by laws of that
|
||||
jurisdiction, without reference to its conflict-of-law provisions.
|
||||
Nothing in this Section shall prevent a party's ability to bring
|
||||
cross-claims or counter-claims.
|
||||
|
||||
9. Miscellaneous
|
||||
----------------
|
||||
|
||||
This License represents the complete agreement concerning the subject
|
||||
matter hereof. If any provision of this License is held to be
|
||||
unenforceable, such provision shall be reformed only to the extent
|
||||
necessary to make it enforceable. Any law or regulation which provides
|
||||
that the language of a contract shall be construed against the drafter
|
||||
shall not be used to construe this License against a Contributor.
|
||||
|
||||
10. Versions of the License
|
||||
---------------------------
|
||||
|
||||
10.1. New Versions
|
||||
|
||||
Mozilla Foundation is the license steward. Except as provided in Section
|
||||
10.3, no one other than the license steward has the right to modify or
|
||||
publish new versions of this License. Each version will be given a
|
||||
distinguishing version number.
|
||||
|
||||
10.2. Effect of New Versions
|
||||
|
||||
You may distribute the Covered Software under the terms of the version
|
||||
of the License under which You originally received the Covered Software,
|
||||
or under the terms of any subsequent version published by the license
|
||||
steward.
|
||||
|
||||
10.3. Modified Versions
|
||||
|
||||
If you create software not governed by this License, and you want to
|
||||
create a new license for such software, you may create and use a
|
||||
modified version of this License if you rename the license and remove
|
||||
any references to the name of the license steward (except to note that
|
||||
such modified license differs from this License).
|
||||
|
||||
10.4. Distributing Source Code Form that is Incompatible With Secondary
|
||||
Licenses
|
||||
|
||||
If You choose to distribute Source Code Form that is Incompatible With
|
||||
Secondary Licenses under the terms of this version of the License, the
|
||||
notice described in Exhibit B of this License must be attached.
|
||||
|
||||
Exhibit A - Source Code Form License Notice
|
||||
-------------------------------------------
|
||||
|
||||
This Source Code Form is subject to the terms of the Mozilla Public
|
||||
License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
If it is not possible or desirable to put the notice in a particular
|
||||
file, then You may include the notice in a location (such as a LICENSE
|
||||
file in a relevant directory) where a recipient would be likely to look
|
||||
for such a notice.
|
||||
|
||||
You may add additional accurate notices of copyright ownership.
|
||||
|
||||
Exhibit B - "Incompatible With Secondary Licenses" Notice
|
||||
---------------------------------------------------------
|
||||
|
||||
This Source Code Form is "Incompatible With Secondary Licenses", as
|
||||
defined by the Mozilla Public License, v. 2.0.
|
|
@ -0,0 +1,104 @@
|
|||
# DevTools Reps
|
||||
|
||||
![](http://g.recordit.co/IxhfRP8pNf.gif)
|
||||
|
||||
Reps is Firefox DevTools' remote object formatter. It stands for _representation_.
|
||||
|
||||
## Example
|
||||
|
||||
```js
|
||||
const React = require("react");
|
||||
let {
|
||||
Rep,
|
||||
Grip,
|
||||
MODE,
|
||||
ObjectInspector,
|
||||
ObjectInspectorUtils
|
||||
} = require("devtools-reps");
|
||||
|
||||
function renderRep({ object, mode }) {
|
||||
return Rep({ object, defaultRep: Grip, mode });
|
||||
}
|
||||
|
||||
ReactDOM.render(
|
||||
Rep({ object, defaultRep: Grip, mode }),
|
||||
document.createElement("div")
|
||||
);
|
||||
```
|
||||
|
||||
## What is what?
|
||||
|
||||
### `Rep`
|
||||
|
||||
`Rep` is the top-level component that is capable of formatting any type.
|
||||
|
||||
Supported types:
|
||||
|
||||
> RegExp, StyleSheet, Event, DateTime, TextNode, Attribute, Func, ArrayRep, Document, Window, ObjectWithText, ObjectWithURL, GripArray, GripMap, Grip, Undefined, Null, StringRep, Number, SymbolRep,
|
||||
|
||||
### `Grip`
|
||||
|
||||
`Grip` is a client representation of a remote JS object and is used as an input object for this rep component.
|
||||
|
||||
## Getting started
|
||||
|
||||
You need to clone the debugger.html repository, then install dependencies, for which you'll need the [Yarn](https://yarnpkg.com/en/) tool:
|
||||
|
||||
```
|
||||
git clone https://github.com/devtools-html/debugger.html.git
|
||||
cd debugger.html
|
||||
yarn install
|
||||
```
|
||||
|
||||
Once everything is installed, you can start the development server with:
|
||||
|
||||
```bash
|
||||
cd packages/devtools-reps/
|
||||
yarn start
|
||||
```
|
||||
|
||||
and navigate to `http://localhost:8000` to access the dashboard.
|
||||
|
||||
## Running the demo app
|
||||
|
||||
Navigating to the above address will have landed you on an empty launchpad UI:
|
||||
|
||||
![Image of empty launchpad](./assets/images/empty-launchpad.png)
|
||||
|
||||
Click on the _Launch Firefox_ button. This should launch Firefox with a dedicated profile, listening for connections on port 6080.
|
||||
|
||||
The UI should update automatically and show you at least one tab for the new Firefox instance. If it doesn't, reload the dashboard.
|
||||
|
||||
![Image of launchpad](./assets/images/launchpad-app.png)
|
||||
|
||||
Click on any of the tabs. This should open the demo app:
|
||||
|
||||
![Image of demo app](./assets/images/demo-app.png)
|
||||
|
||||
Then you can type any expression in the input field. They will be evaluated against the target tab selected in the previous steps (so if there specific objects on window on this webpage, you can check how they are represented with reps etc, ...).
|
||||
|
||||
## Running the tests
|
||||
|
||||
Reps tests are written with jest.
|
||||
|
||||
They are run on every pull request with Circle CI, and you can run them locally by executing `yarn test` from /packages/devtools-reps.
|
||||
|
||||
## History
|
||||
|
||||
The Reps project was ported to Github January 18th, 2017. You can view the history of a file after that date on [github][history] or by running this query:
|
||||
|
||||
```bash
|
||||
git log --before "2017-1-17" devtools/client/shared/components/reps
|
||||
```
|
||||
|
||||
They were first moved to the [devtools-reps][gh-devtools-reps] repository, then to the [devtools-core][gh-devtools-core] one, before being migrated to this repository.
|
||||
|
||||
[history]: https://github.com/mozilla/gecko-dev/commits/master/devtools/client/shared/components/reps
|
||||
[gh-devtools-reps]:
|
||||
https://github.com/devtools-html/reps/commits/master
|
||||
[gh-devtools-core]:
|
||||
https://github.com/devtools-html/devtools-core/commits/5ba3d6f6a44def9978a983edd6f2f89747dca2c7/packages/devtools-reps
|
||||
|
||||
## License
|
||||
|
||||
[MPL 2](./LICENSE)
|
|
@ -0,0 +1 @@
|
|||
The assets directory is mandatory to run `../bin/publish-assets.js`.
|
Двоичные данные
devtools/client/debugger/new/packages/devtools-reps/assets/images/demo-app.png
Normal file
Двоичные данные
devtools/client/debugger/new/packages/devtools-reps/assets/images/demo-app.png
Normal file
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 85 KiB |
Двоичные данные
devtools/client/debugger/new/packages/devtools-reps/assets/images/empty-launchpad.png
Normal file
Двоичные данные
devtools/client/debugger/new/packages/devtools-reps/assets/images/empty-launchpad.png
Normal file
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 70 KiB |
Двоичные данные
devtools/client/debugger/new/packages/devtools-reps/assets/images/launchpad-app.png
Normal file
Двоичные данные
devtools/client/debugger/new/packages/devtools-reps/assets/images/launchpad-app.png
Normal file
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 97 KiB |
|
@ -0,0 +1,25 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
const path = require("path");
|
||||
const toolbox = require("devtools-launchpad/index");
|
||||
const serve = require("express-static");
|
||||
const config = require("../config");
|
||||
|
||||
let webpackConfig = require("../webpack.config");
|
||||
|
||||
let { app } = toolbox.startDevServer(config, webpackConfig, __dirname);
|
||||
|
||||
// Serve devtools-reps images
|
||||
app.use(
|
||||
"/devtools-reps/images/",
|
||||
serve(path.join(__dirname, "../src/shared/images"))
|
||||
);
|
||||
|
||||
// As well as devtools-components ones, with a different path, which we are going to
|
||||
// write in the postCSS config in development mode.
|
||||
app.use(
|
||||
"/devtools-components/images/",
|
||||
serve(path.join(__dirname, "../../../node_modules/devtools-components/src/images"))
|
||||
);
|
|
@ -0,0 +1,16 @@
|
|||
module.exports = {
|
||||
"title": "Reps",
|
||||
"hotReloading": true,
|
||||
"defaultURL": "https://nchevobbe.github.io/demo/console-test-app.html",
|
||||
"environment": "development",
|
||||
"theme": "light",
|
||||
"firefox": {
|
||||
"webSocketConnection": false,
|
||||
"host": "localhost",
|
||||
"webSocketPort": 9000,
|
||||
"tcpPort": 6080,
|
||||
},
|
||||
"development": {
|
||||
"serverPort": 8000
|
||||
}
|
||||
};
|
|
@ -0,0 +1,26 @@
|
|||
const { resolve } = require("path");
|
||||
const rootDir = resolve(__dirname, "src");
|
||||
module.exports = {
|
||||
rootDir,
|
||||
displayName: "devtools-reps test",
|
||||
setupFiles: [
|
||||
"<rootDir>/../../../src/test/__mocks__/request-animation-frame.js",
|
||||
"<rootDir>/test/__mocks__/selection.js",
|
||||
"<rootDir>/test/setup.js"
|
||||
],
|
||||
setupTestFrameworkScriptFile: "<rootDir>/test/setup-file.js",
|
||||
testMatch: ["**/tests/**/*.js"],
|
||||
testPathIgnorePatterns: [
|
||||
"/node_modules/",
|
||||
"<rootDir>/test/",
|
||||
"<rootDir>/reps/tests/test-helpers",
|
||||
"<rootDir>/utils/tests/fixtures/",
|
||||
"<rootDir>/object-inspector/tests/__mocks__/",
|
||||
"<rootDir>/object-inspector/tests/test-utils"
|
||||
],
|
||||
testURL: "http://localhost/",
|
||||
transformIgnorePatterns: ["node_modules/(?!devtools-)"],
|
||||
moduleNameMapper: {
|
||||
"\\.css$": "<rootDir>/../../../src/test/__mocks__/styleMock.js"
|
||||
}
|
||||
};
|
|
@ -0,0 +1,56 @@
|
|||
{
|
||||
"name": "devtools-reps",
|
||||
"version": "0.23.0",
|
||||
"description": "Devtools Reps",
|
||||
"main": "src/index.js",
|
||||
"scripts": {
|
||||
"start": "node bin/dev-server.js",
|
||||
"lint-js": "eslint src",
|
||||
"firefox": "./node_modules/.bin/start-firefox --start --location https://devtools-html.github.io/debugger-examples/",
|
||||
"chrome": "./node_modules/.bin/start-chrome",
|
||||
"license-check": "devtools-license-check",
|
||||
"test": "jest --projects jest.config.js"
|
||||
},
|
||||
"author": "",
|
||||
"license": "MPL-2.0",
|
||||
"repository": {
|
||||
"url": "git://github.com/devtools-html/reps.git",
|
||||
"type": "git"
|
||||
},
|
||||
"engineStrict": true,
|
||||
"engines": {
|
||||
"node": ">=8.9.4"
|
||||
},
|
||||
"dependencies": {
|
||||
"classnames": "^2.2.5",
|
||||
"devtools-components": "^0.6.0",
|
||||
"lodash": "^4.17.2",
|
||||
"prop-types": "^15.6.0",
|
||||
"react": "^16.4.1",
|
||||
"react-dom": "^16.4.1",
|
||||
"react-dom-factories": "^1.0.2",
|
||||
"react-redux": "^5.0.7",
|
||||
"redux": "^3.7.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@sucrase/webpack-object-rest-spread-plugin": "^1.0.0",
|
||||
"babel-plugin-syntax-object-rest-spread": "^6.13.0",
|
||||
"babel-plugin-transform-es2015-modules-commonjs": "^6.26.0",
|
||||
"babel-preset-react": "^6.24.1",
|
||||
"devtools-config": "^0.0.16",
|
||||
"devtools-launchpad": "^0.0.141",
|
||||
"devtools-license-check": "^0.7.0",
|
||||
"devtools-modules": "~1.1.0",
|
||||
"devtools-services": "^0.0.1",
|
||||
"enzyme": "^3.3.0",
|
||||
"enzyme-adapter-react-16": "^1.1.1",
|
||||
"enzyme-to-json": "^3.3.1",
|
||||
"eslint": "^5.0.0",
|
||||
"eslint-plugin-mozilla": "1.0.4",
|
||||
"fs-extra": "^7.0.0",
|
||||
"immutable": "^3.8.2",
|
||||
"postcss-url-mapper": "^1.2.0",
|
||||
"react-immutable-proptypes": "^2.1.0",
|
||||
"redux-logger": "=3.0.6"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
const mapUrl = require("postcss-url-mapper");
|
||||
const MC_PATH = "resource://devtools/client/shared/components/reps/images/";
|
||||
const EXPRESS_PATH = "/devtools-reps/images/";
|
||||
const IMAGES = ["open-inspector.svg", "jump-definition.svg", "input.svg"];
|
||||
|
||||
|
||||
function mapUrlProduction(url, type) {
|
||||
for (const img of IMAGES) {
|
||||
url = url.replace(`/images/${img}`, `${MC_PATH}${img}`);
|
||||
}
|
||||
return url;
|
||||
}
|
||||
|
||||
function mapUrlDevelopment(url) {
|
||||
for (const img of IMAGES) {
|
||||
url = url.replace(`/images/${img}`, `${EXPRESS_PATH}${img}`);
|
||||
}
|
||||
return url;
|
||||
}
|
||||
|
||||
module.exports = ({ file, options, env }) => {
|
||||
if (env === "production") {
|
||||
return {
|
||||
plugins: [mapUrl(mapUrlProduction)]
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
plugins: [mapUrl(mapUrlDevelopment)]
|
||||
};
|
||||
};
|
|
@ -0,0 +1,25 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
|
||||
|
||||
const { MODE } = require("./reps/constants");
|
||||
const { REPS, getRep } = require("./reps/rep");
|
||||
const objectInspector = require("./object-inspector");
|
||||
|
||||
const {
|
||||
parseURLEncodedText,
|
||||
parseURLParams,
|
||||
maybeEscapePropertyName,
|
||||
getGripPreviewItems
|
||||
} = require("./reps/rep-utils");
|
||||
|
||||
module.exports = {
|
||||
REPS,
|
||||
getRep,
|
||||
MODE,
|
||||
maybeEscapePropertyName,
|
||||
parseURLEncodedText,
|
||||
parseURLParams,
|
||||
getGripPreviewItems,
|
||||
objectInspector
|
||||
};
|
|
@ -0,0 +1,76 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
|
||||
|
||||
const constants = require("../constants");
|
||||
const { generateKey } = require("../utils/utils");
|
||||
|
||||
function evaluateInput(input) {
|
||||
return async function({ dispatch, client }) {
|
||||
try {
|
||||
const packet = await client.clientCommands.evaluate(input);
|
||||
dispatch(addExpression(input, packet));
|
||||
} catch (err) {
|
||||
console.warn("Error when evaluating expression", err);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function addExpression(input, packet) {
|
||||
return {
|
||||
key: generateKey(),
|
||||
type: constants.ADD_EXPRESSION,
|
||||
value: {
|
||||
input,
|
||||
packet
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function clearExpressions() {
|
||||
return {
|
||||
type: constants.CLEAR_EXPRESSIONS
|
||||
};
|
||||
}
|
||||
|
||||
function showResultPacket(key) {
|
||||
return {
|
||||
key,
|
||||
type: constants.SHOW_RESULT_PACKET
|
||||
};
|
||||
}
|
||||
|
||||
function hideResultPacket(key) {
|
||||
return {
|
||||
key,
|
||||
type: constants.HIDE_RESULT_PACKET
|
||||
};
|
||||
}
|
||||
|
||||
function createObjectClient(grip) {
|
||||
return function({ dispatch, client }) {
|
||||
return client.getObjectClient(grip);
|
||||
};
|
||||
}
|
||||
|
||||
function createLongStringClient(grip) {
|
||||
return function({ dispatch, client }) {
|
||||
return client.getLongStringClient(grip);
|
||||
};
|
||||
}
|
||||
|
||||
function releaseActor(actor) {
|
||||
return function({ dispatch, client }) {
|
||||
client.releaseActor(actor);
|
||||
};
|
||||
}
|
||||
module.exports = {
|
||||
addExpression,
|
||||
clearExpressions,
|
||||
evaluateInput,
|
||||
showResultPacket,
|
||||
hideResultPacket,
|
||||
createObjectClient,
|
||||
createLongStringClient,
|
||||
releaseActor
|
||||
};
|
|
@ -0,0 +1,11 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
|
||||
|
||||
const expressions = require("./expressions");
|
||||
const input = require("./input");
|
||||
|
||||
module.exports = {
|
||||
...expressions,
|
||||
...input
|
||||
};
|
|
@ -0,0 +1,39 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
|
||||
|
||||
const constants = require("../constants");
|
||||
const expressionsActions = require("./expressions");
|
||||
const { generateKey } = require("../utils/utils");
|
||||
|
||||
function addInput(input) {
|
||||
return ({ dispatch }) => {
|
||||
dispatch(expressionsActions.evaluateInput(input));
|
||||
|
||||
dispatch({
|
||||
key: generateKey(),
|
||||
type: constants.ADD_INPUT,
|
||||
value: input
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
function changeCurrentInput(input) {
|
||||
return {
|
||||
type: constants.CHANGE_CURRENT_INPUT,
|
||||
value: input
|
||||
};
|
||||
}
|
||||
|
||||
function navigateInputHistory(dir) {
|
||||
return {
|
||||
type: constants.NAVIGATE_INPUT_HISTORY,
|
||||
value: dir
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
addInput,
|
||||
changeCurrentInput,
|
||||
navigateInputHistory
|
||||
};
|
|
@ -0,0 +1,89 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
main {
|
||||
--default-border: 1px solid var(--theme-splitter-color);
|
||||
height: 100vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.expressions {
|
||||
padding: 2em 1em;
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.rep-row {
|
||||
border: var(--default-border);
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
.rep-input {
|
||||
font-family: monospace;
|
||||
background-color: var(--theme-toolbar-background-alt);
|
||||
color: var(--theme-body-color);
|
||||
padding: 0.5rem;
|
||||
}
|
||||
|
||||
.rep-input:before {
|
||||
content: "➜ ";
|
||||
}
|
||||
|
||||
.reps {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
padding: 1rem;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.rep-element {
|
||||
white-space: pre-wrap;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
.rep-element + .rep-element {
|
||||
margin-left: 0.5rem;
|
||||
}
|
||||
|
||||
.rep-element[data-mode]::before {
|
||||
content: attr(data-mode)":";
|
||||
background-color: var(--theme-toolbar-background);
|
||||
font-family: monospace;
|
||||
display: inline-block;
|
||||
font-size: 0.75rem;
|
||||
padding: 0.1rem 0.25rem;
|
||||
margin-right: 0.25rem;
|
||||
border-radius: 0.25rem;
|
||||
}
|
||||
|
||||
.packet header {
|
||||
display: flex;
|
||||
background-color: var(--theme-toolbar-background-alt);
|
||||
border-top: var(--default-border);
|
||||
color: var(--theme-body-color);
|
||||
padding: 0.5rem;
|
||||
}
|
||||
|
||||
.packet header::before {
|
||||
display: inline-block;
|
||||
padding-right: 0.5rem;
|
||||
}
|
||||
|
||||
.packet header.packet-expanded::before {
|
||||
content: "−";
|
||||
}
|
||||
|
||||
.packet header.packet-collapsed::before {
|
||||
content: "+";
|
||||
}
|
||||
|
||||
.copy-packet-button {
|
||||
margin-left: auto;
|
||||
margin-right: 0.5rem;
|
||||
}
|
||||
|
||||
.packet .packet-rep {
|
||||
padding: 1rem;
|
||||
}
|
|
@ -0,0 +1,90 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
|
||||
|
||||
const React = require("react");
|
||||
const { Component, createFactory } = React;
|
||||
const PropTypes = require("prop-types");
|
||||
const dom = require("react-dom-factories");
|
||||
const { KeyShortcuts } = require("devtools-modules");
|
||||
const shortcuts = new KeyShortcuts({ window });
|
||||
|
||||
const { connect } = require("react-redux");
|
||||
const { bindActionCreators } = require("redux");
|
||||
const selectors = require("../selectors");
|
||||
|
||||
const Header = createFactory(require("./Header"));
|
||||
const ResultsList = createFactory(require("./ResultsList"));
|
||||
|
||||
require("./Console.css");
|
||||
|
||||
class Console extends Component {
|
||||
static get propTypes() {
|
||||
return {
|
||||
addInput: PropTypes.func.isRequired,
|
||||
changeCurrentInput: PropTypes.func.isRequired,
|
||||
clearExpressions: PropTypes.func.isRequired,
|
||||
currentInputValue: PropTypes.string.isRequired,
|
||||
evaluateInput: PropTypes.func.isRequired,
|
||||
expressions: PropTypes.object.isRequired,
|
||||
hideResultPacket: PropTypes.func.isRequired,
|
||||
navigateInputHistory: PropTypes.func.isRequired,
|
||||
showResultPacket: PropTypes.func.isRequired
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
shortcuts.on("CmdOrCtrl+Shift+L", this.props.clearExpressions);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
shortcuts.off("CmdOrCtrl+Shift+L");
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
addInput,
|
||||
changeCurrentInput,
|
||||
clearExpressions,
|
||||
currentInputValue,
|
||||
evaluateInput,
|
||||
expressions,
|
||||
hideResultPacket,
|
||||
navigateInputHistory,
|
||||
showResultPacket
|
||||
} = this.props;
|
||||
|
||||
return dom.main(
|
||||
{},
|
||||
Header({
|
||||
addInput,
|
||||
changeCurrentInput,
|
||||
clearResultsList: clearExpressions,
|
||||
currentInputValue,
|
||||
evaluate: evaluateInput,
|
||||
navigateInputHistory
|
||||
}),
|
||||
ResultsList({
|
||||
expressions: expressions.reverse(),
|
||||
hideResultPacket,
|
||||
showResultPacket
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function mapStateToProps(state) {
|
||||
return {
|
||||
expressions: selectors.getExpressions(state),
|
||||
currentInputValue: selectors.getCurrentInputValue(state)
|
||||
};
|
||||
}
|
||||
|
||||
function mapDispatchToProps(dispatch) {
|
||||
return bindActionCreators(require("../actions"), dispatch);
|
||||
}
|
||||
|
||||
module.exports = connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
)(Console);
|
|
@ -0,0 +1,46 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
.console-header {
|
||||
background: var(--theme-highlight-pink);
|
||||
color: white;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.console-header form {
|
||||
display: flex;
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.console-header h1 {
|
||||
font-size: 14px;
|
||||
font-weight: 100;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.console-header input {
|
||||
background-color: var(--theme-highlight-pink);
|
||||
flex: 1;
|
||||
border: 1px solid white;
|
||||
color: white;
|
||||
font-size: 1.25rem;
|
||||
padding: 0.25rem;
|
||||
line-height: 2rem;
|
||||
padding-left: 1rem;
|
||||
margin: 0 1rem;
|
||||
}
|
||||
|
||||
.console-header ::-webkit-input-placeholder {
|
||||
color: white;
|
||||
font-size: 1rem;
|
||||
line-height: 2rem;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.console-header .clear-button {
|
||||
background: rgba(255, 255, 255, 0.25);
|
||||
border: 1px solid rgba(255, 255, 255, 0.25);
|
||||
color: white;
|
||||
font-size: 1.2rem;
|
||||
}
|
|
@ -0,0 +1,87 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
|
||||
|
||||
const React = require("react");
|
||||
const { Component, createFactory } = React;
|
||||
const PropTypes = require("prop-types");
|
||||
const dom = require("react-dom-factories");
|
||||
const constants = require("../constants");
|
||||
const QuickLinks = createFactory(require("./QuickLinks"));
|
||||
require("./Header.css");
|
||||
|
||||
class Header extends Component {
|
||||
static get propTypes() {
|
||||
return {
|
||||
addInput: PropTypes.func.isRequired,
|
||||
changeCurrentInput: PropTypes.func.isRequired,
|
||||
clearResultsList: PropTypes.func.isRequired,
|
||||
currentInputValue: PropTypes.string,
|
||||
evaluate: PropTypes.func.isRequired,
|
||||
navigateInputHistory: PropTypes.func.isRequired
|
||||
};
|
||||
}
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.onSubmitForm = this.onSubmitForm.bind(this);
|
||||
this.onInputChange = this.onInputChange.bind(this);
|
||||
this.onInputKeyDown = this.onInputKeyDown.bind(this);
|
||||
this.onClearButtonClick = this.onClearButtonClick.bind(this);
|
||||
}
|
||||
|
||||
onSubmitForm(e) {
|
||||
e.preventDefault();
|
||||
const data = new FormData(e.target);
|
||||
const expression = data.get("expression");
|
||||
this.props.addInput(expression);
|
||||
}
|
||||
|
||||
onInputChange(e) {
|
||||
this.props.changeCurrentInput(e.target.value);
|
||||
}
|
||||
|
||||
onInputKeyDown(e) {
|
||||
if (["ArrowUp", "ArrowDown"].includes(e.key)) {
|
||||
this.props.navigateInputHistory(
|
||||
e.key === "ArrowUp" ? constants.DIR_BACKWARD : constants.DIR_FORWARD
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
onClearButtonClick(e) {
|
||||
this.props.clearResultsList();
|
||||
}
|
||||
|
||||
render() {
|
||||
const { currentInputValue, evaluate } = this.props;
|
||||
|
||||
return dom.header(
|
||||
{ className: "console-header" },
|
||||
dom.form(
|
||||
{ onSubmit: this.onSubmitForm },
|
||||
dom.h1({}, "Reps"),
|
||||
dom.input({
|
||||
type: "text",
|
||||
placeholder: "Enter an expression",
|
||||
name: "expression",
|
||||
value: currentInputValue || "",
|
||||
autoFocus: true,
|
||||
onChange: this.onInputChange,
|
||||
onKeyDown: this.onInputKeyDown
|
||||
}),
|
||||
dom.button(
|
||||
{
|
||||
className: "clear-button",
|
||||
type: "button",
|
||||
onClick: this.onClearButtonClick
|
||||
},
|
||||
"Clear"
|
||||
)
|
||||
),
|
||||
QuickLinks({ evaluate })
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Header;
|
|
@ -0,0 +1,20 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
.quick-links {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
flex-wrap: wrap;
|
||||
background: rgba(255, 255, 255, 0.25);
|
||||
padding: 0.5rem 1rem;
|
||||
}
|
||||
|
||||
.quick-links button {
|
||||
font-size: 1.2rem;
|
||||
background: none;
|
||||
border: none;
|
||||
color: white;
|
||||
margin: 0.5rem 0.25rem;
|
||||
cursor: pointer;
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
|
||||
|
||||
const React = require("react");
|
||||
const { Component } = React;
|
||||
const PropTypes = require("prop-types");
|
||||
const dom = require("react-dom-factories");
|
||||
require("./QuickLinks.css");
|
||||
const samples = require("../samples.js");
|
||||
|
||||
class QuickLinks extends Component {
|
||||
static get propTypes() {
|
||||
return {
|
||||
evaluate: PropTypes.func.isRequired
|
||||
};
|
||||
}
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.evaluateExpressions = this.evaluateExpressions.bind(this);
|
||||
this.renderLinks = this.renderLinks.bind(this);
|
||||
}
|
||||
|
||||
evaluateExpressions(expressions) {
|
||||
expressions.forEach(expression => this.props.evaluate(expression));
|
||||
}
|
||||
|
||||
renderLinks() {
|
||||
return Object.keys(samples).map(label => {
|
||||
const expressions = samples[label];
|
||||
const length = expressions.length;
|
||||
return dom.button(
|
||||
{
|
||||
key: label,
|
||||
title:
|
||||
label === "yolo"
|
||||
? "Add all sample expressions"
|
||||
: `Add ${length} ${label} sample expression${
|
||||
length > 1 ? "s" : ""
|
||||
}`,
|
||||
onClick: () => this.evaluateExpressions(expressions)
|
||||
},
|
||||
label
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
return dom.div({ className: "quick-links" }, this.renderLinks());
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = QuickLinks;
|
|
@ -0,0 +1,145 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
|
||||
|
||||
const React = require("react");
|
||||
const { Component } = React;
|
||||
const PropTypes = require("prop-types");
|
||||
const dom = require("react-dom-factories");
|
||||
const { MODE } = require("../../reps/constants");
|
||||
const { ObjectInspector } = require("../../index").objectInspector;
|
||||
const { Rep } = require("../../reps/rep");
|
||||
|
||||
class Result extends Component {
|
||||
static get propTypes() {
|
||||
return {
|
||||
expression: PropTypes.object.isRequired,
|
||||
showResultPacket: PropTypes.func.isRequired,
|
||||
hideResultPacket: PropTypes.func.isRequired
|
||||
};
|
||||
}
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.copyPacketToClipboard = this.copyPacketToClipboard.bind(this);
|
||||
this.onHeaderClick = this.onHeaderClick.bind(this);
|
||||
this.renderRepInAllModes = this.renderRepInAllModes.bind(this);
|
||||
this.renderRep = this.renderRep.bind(this);
|
||||
this.renderPacket = this.renderPacket.bind(this);
|
||||
}
|
||||
|
||||
copyPacketToClipboard(e, packet) {
|
||||
e.stopPropagation();
|
||||
|
||||
const textField = document.createElement("textarea");
|
||||
textField.innerHTML = JSON.stringify(packet, null, " ");
|
||||
document.body.appendChild(textField);
|
||||
textField.select();
|
||||
document.execCommand("copy");
|
||||
textField.remove();
|
||||
}
|
||||
|
||||
onHeaderClick() {
|
||||
const { expression } = this.props;
|
||||
if (expression.showPacket === true) {
|
||||
this.props.hideResultPacket();
|
||||
} else {
|
||||
this.props.showResultPacket();
|
||||
}
|
||||
}
|
||||
|
||||
renderRepInAllModes({ object }) {
|
||||
return Object.keys(MODE).map(modeKey =>
|
||||
this.renderRep({ object, modeKey })
|
||||
);
|
||||
}
|
||||
|
||||
renderRep({ object, modeKey }) {
|
||||
const path = Symbol(modeKey + object.actor);
|
||||
|
||||
return dom.div(
|
||||
{
|
||||
className: "rep-element",
|
||||
key: path.toString(),
|
||||
"data-mode": modeKey
|
||||
},
|
||||
ObjectInspector({
|
||||
roots: [
|
||||
{
|
||||
path,
|
||||
contents: {
|
||||
value: object
|
||||
}
|
||||
}
|
||||
],
|
||||
autoExpandDepth: 0,
|
||||
mode: MODE[modeKey],
|
||||
// The following properties are optional function props called by the
|
||||
// objectInspector on some occasions. Here we pass dull functions that
|
||||
// only logs the parameters with which the callback was called.
|
||||
onCmdCtrlClick: (node, { depth, event, focused, expanded }) =>
|
||||
console.log("CmdCtrlClick", {
|
||||
node,
|
||||
depth,
|
||||
event,
|
||||
focused,
|
||||
expanded
|
||||
}),
|
||||
onInspectIconClick: nodeFront =>
|
||||
console.log("inspectIcon click", { nodeFront }),
|
||||
onViewSourceInDebugger: location =>
|
||||
console.log("onViewSourceInDebugger", { location }),
|
||||
recordTelemetryEvent: (eventName, extra = {}) => {
|
||||
console.log("📊", eventName, "📊", extra);
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
renderPacket(expression) {
|
||||
const { packet, showPacket } = expression;
|
||||
const headerClassName = showPacket ? "packet-expanded" : "packet-collapsed";
|
||||
const headerLabel = showPacket
|
||||
? "Hide expression packet"
|
||||
: "Show expression packet";
|
||||
|
||||
return dom.div(
|
||||
{ className: "packet" },
|
||||
dom.header(
|
||||
{
|
||||
className: headerClassName,
|
||||
onClick: this.onHeaderClick
|
||||
},
|
||||
headerLabel,
|
||||
showPacket &&
|
||||
dom.button(
|
||||
{
|
||||
className: "copy-packet-button",
|
||||
onClick: e => this.copyPacketToClipboard(e, packet)
|
||||
},
|
||||
"Copy as JSON"
|
||||
)
|
||||
),
|
||||
showPacket &&
|
||||
dom.div({ className: "packet-rep" }, Rep({ object: packet }))
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
const { expression } = this.props;
|
||||
const { input, packet } = expression;
|
||||
return dom.div(
|
||||
{ className: "rep-row" },
|
||||
dom.div({ className: "rep-input" }, input),
|
||||
dom.div(
|
||||
{ className: "reps" },
|
||||
this.renderRepInAllModes({
|
||||
object: packet.exception || packet.result
|
||||
})
|
||||
),
|
||||
this.renderPacket(expression)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Result;
|
|
@ -0,0 +1,42 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
|
||||
|
||||
const React = require("react");
|
||||
const { Component, createFactory } = React;
|
||||
const PropTypes = require("prop-types");
|
||||
const dom = require("react-dom-factories");
|
||||
const ImPropTypes = require("react-immutable-proptypes");
|
||||
|
||||
const Result = createFactory(require("./Result"));
|
||||
|
||||
class ResultsList extends Component {
|
||||
static get propTypes() {
|
||||
return {
|
||||
expressions: ImPropTypes.map.isRequired,
|
||||
showResultPacket: PropTypes.func.isRequired,
|
||||
hideResultPacket: PropTypes.func.isRequired
|
||||
};
|
||||
}
|
||||
|
||||
render() {
|
||||
const { expressions, showResultPacket, hideResultPacket } = this.props;
|
||||
|
||||
return dom.div(
|
||||
{ className: "expressions" },
|
||||
expressions
|
||||
.entrySeq()
|
||||
.toJS()
|
||||
.map(([key, expression]) =>
|
||||
Result({
|
||||
key,
|
||||
expression: expression.toJS(),
|
||||
showResultPacket: () => showResultPacket(key),
|
||||
hideResultPacket: () => hideResultPacket(key)
|
||||
})
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = ResultsList;
|
|
@ -0,0 +1,19 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
|
||||
|
||||
module.exports = {
|
||||
ADD_EXPRESSION: Symbol("ADD_EXPRESSION"),
|
||||
LOAD_PROPERTIES: Symbol("LOAD_PROPERTIES"),
|
||||
LOAD_ENTRIES: Symbol("LOAD_ENTRIES"),
|
||||
EVALUATE_EXPRESSION: Symbol("EVALUATE_EXPRESSION"),
|
||||
CLEAR_EXPRESSIONS: Symbol("CLEAR_EXPRESSIONS"),
|
||||
SHOW_RESULT_PACKET: Symbol("SHOW_RESULT_PACKET"),
|
||||
HIDE_RESULT_PACKET: Symbol("HIDE_RESULT_PACKET"),
|
||||
ADD_INPUT: Symbol("ADD_INPUT"),
|
||||
CHANGE_CURRENT_INPUT: Symbol("CHANGE_CURRENT_INPUT"),
|
||||
NAVIGATE_INPUT_HISTORY: Symbol("NAVIGATE_INPUT_HISTORY"),
|
||||
DIR_FORWARD: Symbol("DIR_FORWARD "),
|
||||
DIR_BACKWARD: Symbol("DIR_BACKWARD"),
|
||||
LS_EXPRESSIONS_KEY: "LS_EXPRESSIONS_KEY"
|
||||
};
|
|
@ -0,0 +1,62 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
|
||||
|
||||
// globals window, document
|
||||
|
||||
require("../reps/reps.css");
|
||||
|
||||
const React = require("react");
|
||||
const ReactDOM = require("react-dom");
|
||||
|
||||
const { bootstrap, renderRoot } = require("devtools-launchpad");
|
||||
|
||||
const RepsConsole = require("./components/Console");
|
||||
const { configureStore } = require("./store");
|
||||
|
||||
require("./launchpad.css");
|
||||
|
||||
function onConnect(connection) {
|
||||
if (!connection) {
|
||||
return;
|
||||
}
|
||||
|
||||
const client = {
|
||||
clientCommands: {
|
||||
evaluate: input =>
|
||||
new Promise(resolve => {
|
||||
connection.tabConnection.tabTarget.activeConsole.evaluateJS(
|
||||
input,
|
||||
result => resolve(result)
|
||||
);
|
||||
})
|
||||
},
|
||||
|
||||
createObjectClient: function(grip) {
|
||||
return connection.tabConnection.threadClient.pauseGrip(grip);
|
||||
},
|
||||
createLongStringClient: function(grip) {
|
||||
return connection.tabConnection.tabTarget.activeConsole.longString(grip);
|
||||
},
|
||||
releaseActor: function(actor) {
|
||||
return connection.tabConnection.debuggerClient.release(actor);
|
||||
}
|
||||
};
|
||||
|
||||
const store = configureStore({
|
||||
makeThunkArgs: (args, state) => ({ ...args, client }),
|
||||
client
|
||||
});
|
||||
renderRoot(React, ReactDOM, RepsConsole, store);
|
||||
}
|
||||
|
||||
function onConnectionError(e) {
|
||||
const h1 = document.createElement("h1");
|
||||
h1.innerText = `An error occured during the connection: «${e.message}»`;
|
||||
console.warn("An error occured during the connection", e);
|
||||
renderRoot(React, ReactDOM, h1);
|
||||
}
|
||||
|
||||
bootstrap(React, ReactDOM)
|
||||
.then(onConnect, onConnectionError)
|
||||
.catch(onConnectionError);
|
|
@ -0,0 +1,13 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
html,
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
|
||||
|
||||
const constants = require("../constants");
|
||||
const Immutable = require("immutable");
|
||||
|
||||
const initialState = Immutable.Map();
|
||||
|
||||
function update(state = initialState, action) {
|
||||
const { type, value, key } = action;
|
||||
|
||||
switch (type) {
|
||||
case constants.ADD_EXPRESSION:
|
||||
const newState = state.set(key, Immutable.Map(value));
|
||||
window.localStorage.setItem(
|
||||
constants.LS_EXPRESSIONS_KEY,
|
||||
JSON.stringify(newState.toJS())
|
||||
);
|
||||
return newState;
|
||||
|
||||
case constants.CLEAR_EXPRESSIONS:
|
||||
window.localStorage.removeItem(constants.LS_EXPRESSIONS_KEY);
|
||||
return state.clear();
|
||||
|
||||
case constants.SHOW_RESULT_PACKET:
|
||||
return state.mergeIn([key], { showPacket: true });
|
||||
|
||||
case constants.HIDE_RESULT_PACKET:
|
||||
return state.mergeIn([key], { showPacket: false });
|
||||
}
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
module.exports = update;
|
|
@ -0,0 +1,13 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
|
||||
|
||||
const expressions = require("./expressions");
|
||||
const input = require("./input");
|
||||
const { objectInspector } = require("../../index");
|
||||
|
||||
module.exports = {
|
||||
expressions,
|
||||
input,
|
||||
objectInspector: objectInspector.reducer.default
|
||||
};
|
|
@ -0,0 +1,63 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
|
||||
|
||||
const constants = require("../constants");
|
||||
const Immutable = require("immutable");
|
||||
|
||||
const initialState = Immutable.Map({
|
||||
currentValue: "",
|
||||
currentNavigationKey: null,
|
||||
history: Immutable.OrderedMap()
|
||||
});
|
||||
|
||||
function update(state = initialState, action) {
|
||||
const { type, value, key } = action;
|
||||
|
||||
const currentValue = state.get("currentValue");
|
||||
const currentNavigationKey = state.get("currentNavigationKey");
|
||||
const history = state.get("history");
|
||||
|
||||
switch (type) {
|
||||
case constants.ADD_INPUT:
|
||||
return state.withMutations(map => {
|
||||
map
|
||||
.set("history", history.set(key, value))
|
||||
.set("currentValue", "")
|
||||
.set("currentNavigationKey", null);
|
||||
});
|
||||
|
||||
case constants.CHANGE_CURRENT_INPUT:
|
||||
return state.set("currentValue", value);
|
||||
|
||||
case constants.NAVIGATE_INPUT_HISTORY:
|
||||
const keys = history.reverse().keySeq();
|
||||
const navigationIndex = keys.indexOf(currentNavigationKey);
|
||||
const dir = value;
|
||||
|
||||
const newNavigationIndex =
|
||||
dir === constants.DIR_BACKWARD
|
||||
? navigationIndex + 1
|
||||
: navigationIndex - 1;
|
||||
|
||||
const newNavigationKey =
|
||||
newNavigationIndex >= 0 ? keys.get(newNavigationIndex) : null;
|
||||
|
||||
const fallbackValue = dir === constants.DIR_BACKWARD ? currentValue : "";
|
||||
const fallbackNavigationKey =
|
||||
dir === constants.DIR_BACKWARD ? currentNavigationKey : -1;
|
||||
|
||||
return state.withMutations(map => {
|
||||
map
|
||||
.set("currentValue", history.get(newNavigationKey) || fallbackValue)
|
||||
.set(
|
||||
"currentNavigationKey",
|
||||
newNavigationKey || fallbackNavigationKey
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
module.exports = update;
|
|
@ -0,0 +1,196 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
|
||||
|
||||
const samples = {
|
||||
array: ['x = [1, "2", {three: 3}, []]', "x = []"],
|
||||
|
||||
boolean: ["true", "false"],
|
||||
|
||||
date: ["new Date()"],
|
||||
|
||||
function: ["x = () => { 2 }"],
|
||||
|
||||
node: [
|
||||
`x = document.createElement("div");
|
||||
x.setAttribute("id", "myNodeId");
|
||||
x.setAttribute("class", "my-class and another");
|
||||
x.textContent = "My node id";
|
||||
x;`,
|
||||
`x = document.createElementNS("http://www.w3.org/2000/svg", "clipPath");
|
||||
x.setAttribute("id", "myNodeId");
|
||||
x.setAttribute("class", "my-class and another");
|
||||
x;`,
|
||||
"document.createComment('my comment node')",
|
||||
"document.createTextNode('foo')",
|
||||
`x = document.createAttribute('foo');
|
||||
x.value = "bar";
|
||||
x;`
|
||||
],
|
||||
|
||||
"map & sets": [
|
||||
`
|
||||
({
|
||||
"small set": new Set([1,2,3,4]),
|
||||
"small map": new Map([
|
||||
["a", {suba: 1}],
|
||||
[{bkey: "b"}, 2]]
|
||||
),
|
||||
"medium set": new Set(
|
||||
Array.from({length: 20})
|
||||
.map((el, i) => ({
|
||||
[String.fromCharCode(65 + i)]: i + 1,
|
||||
test: {
|
||||
[i] : "item" + i
|
||||
}
|
||||
})
|
||||
)),
|
||||
"medium map": new Map(
|
||||
Array
|
||||
.from({length: 20})
|
||||
.map((el, i) => [
|
||||
{
|
||||
[String.fromCharCode(65 + i)]: i + 1,
|
||||
test: {[i] : "item" + i, body: document.body}
|
||||
},
|
||||
Symbol(i + 1)
|
||||
])
|
||||
),
|
||||
"large set": new Set(
|
||||
Array.from({length: 2000})
|
||||
.map((el, i) => ({
|
||||
[String.fromCharCode(65 + i)]: i + 1,
|
||||
test: {
|
||||
[i] : "item" + i
|
||||
}
|
||||
})
|
||||
)),
|
||||
"large map": new Map(
|
||||
Array
|
||||
.from({length: 2000})
|
||||
.map((el, i) => [
|
||||
{
|
||||
[String.fromCharCode(65 + i)]: i + 1,
|
||||
document
|
||||
},
|
||||
Symbol(i + 1)
|
||||
])
|
||||
),
|
||||
})
|
||||
`
|
||||
],
|
||||
|
||||
number: ["1", "-1", "-3.14", "0", "-0", "Infinity", "-Infinity", "NaN"],
|
||||
|
||||
object: [
|
||||
"x = {a: 2}",
|
||||
`
|
||||
Object.create(null, Object.getOwnPropertyDescriptors({
|
||||
get myStringGetter() {
|
||||
return "hello"
|
||||
},
|
||||
get myNumberGetter() {
|
||||
return 123;
|
||||
},
|
||||
get myUndefinedGetter() {
|
||||
return undefined;
|
||||
},
|
||||
get myNullGetter() {
|
||||
return null;
|
||||
},
|
||||
get myObjectGetter() {
|
||||
return {foo: "bar"}
|
||||
},
|
||||
get myArrayGetter() {
|
||||
return Array.from({length: 100000}, (_, i) => i)
|
||||
},
|
||||
get myMapGetter() {
|
||||
return new Map([["foo", {bar: "baz"}]])
|
||||
},
|
||||
get mySetGetter() {
|
||||
return new Set([1, {bar: "baz"}]);
|
||||
},
|
||||
get myProxyGetter() {
|
||||
var handler = { get: function(target, name) {
|
||||
return name in target ? target[name] : 37; }
|
||||
};
|
||||
return new Proxy({a: 1}, handler);
|
||||
},
|
||||
get myThrowingGetter() {
|
||||
return a.b.c.d.e.f;
|
||||
},
|
||||
get myLongStringGetter() {
|
||||
return "ab ".repeat(1e5)
|
||||
},
|
||||
set mySetterOnly(x) {}
|
||||
}))
|
||||
`
|
||||
],
|
||||
|
||||
promise: [
|
||||
"Promise.resolve([1, 2, 3])",
|
||||
"Promise.reject(new Error('This is wrong'))",
|
||||
"new Promise(() => {})"
|
||||
],
|
||||
|
||||
proxy: [
|
||||
`
|
||||
var handler = {
|
||||
get: function(target, name) {
|
||||
return name in target ?
|
||||
target[name] :
|
||||
37;
|
||||
}
|
||||
};
|
||||
new Proxy({a: 1}, handler);
|
||||
`
|
||||
],
|
||||
|
||||
regexp: ["new RegExp('^[-]?[0-9]+[.]?[0-9]+$')"],
|
||||
|
||||
string: [
|
||||
"'foo'",
|
||||
"'bar\nbaz\nyup'",
|
||||
"'http://example.com'",
|
||||
"'blah'.repeat(10000)",
|
||||
"'http://example.com '.repeat(1000)"
|
||||
],
|
||||
|
||||
symbol: ["Symbol('foo')", "Symbol()"],
|
||||
|
||||
errors: [
|
||||
"throw new Error('This is a simple error message.');",
|
||||
`
|
||||
var error = new Error('Complicated error message');
|
||||
error.stack =
|
||||
"unserializeProfileOfArbitraryFormat@http://localhost:4242/2969802751c9e11c0c2d.bundle.js:26705:11\\n" +
|
||||
"_callee7$@http://localhost:4242/2969802751c9e11c0c2d.bundle.js:27948:27\\n" +
|
||||
"tryCatch@http://localhost:4242/2969802751c9e11c0c2d.bundle.js:64198:37\\n" +
|
||||
"invoke@http://localhost:4242/2969802751c9e11c0c2d.bundle.js:64432:22\\n" +
|
||||
"defineIteratorMethods/</prototype[method]@http://localhost:4242/2969802751c9e11c0c2d.bundle.js:64250:16\\n" +
|
||||
"step@http://localhost:4242/2969802751c9e11c0c2d.bundle.js:5257:22\\n" +
|
||||
"step/<@http://localhost:4242/2969802751c9e11c0c2d.bundle.js:5268:13\\n" +
|
||||
"run@http://localhost:4242/2969802751c9e11c0c2d.bundle.js:64973:22\\n" +
|
||||
"notify/<@http://localhost:4242/2969802751c9e11c0c2d.bundle.js:64986:28\\n" +
|
||||
"flush@http://localhost:4242/2969802751c9e11c0c2d.bundle.js:65282:9\\n";
|
||||
|
||||
error;
|
||||
`,
|
||||
`
|
||||
var error = new Error('Complicated error message');
|
||||
error.stack =
|
||||
"onPacket@resource://devtools/shared/base-loader.js -> resource://devtools/shared/client/debugger-client.js:856:9\\n" +
|
||||
"send/<@resource://devtools/shared/base-loader.js -> resource://devtools/shared/transport/transport.js:569:13\\n" +
|
||||
"exports.makeInfallible/<@resource://devtools/shared/base-loader.js -> resource://devtools/shared/ThreadSafeDevToolsUtils.js:109:14\\n" +
|
||||
"exports.makeInfallible/<@resource://devtools/shared/base-loader.js -> resource://devtools/shared/ThreadSafeDevToolsUtils.js:109:14\\n";
|
||||
|
||||
error;
|
||||
`
|
||||
]
|
||||
};
|
||||
|
||||
samples.yolo = Object.keys(samples).reduce((res, key) => {
|
||||
return [...res, ...samples[key]];
|
||||
}, []);
|
||||
|
||||
module.exports = samples;
|
|
@ -0,0 +1,20 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
|
||||
|
||||
function getExpressions(state) {
|
||||
return state.expressions;
|
||||
}
|
||||
|
||||
function getInputState(state) {
|
||||
return state.input;
|
||||
}
|
||||
|
||||
function getCurrentInputValue(state) {
|
||||
return getInputState(state).get("currentValue");
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
getCurrentInputValue,
|
||||
getExpressions
|
||||
};
|
|
@ -0,0 +1,21 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
|
||||
|
||||
const { applyMiddleware, combineReducers, createStore } = require("redux");
|
||||
|
||||
const { logger } = require("redux-logger");
|
||||
const { promise } = require("./utils/redux/middleware/promise");
|
||||
const { thunk } = require("./utils/redux/middleware/thunk");
|
||||
const reducers = require("./reducers");
|
||||
|
||||
function configureStore(options, client) {
|
||||
return createStore(
|
||||
combineReducers(reducers),
|
||||
applyMiddleware(thunk(options.makeThunkArgs), promise, logger)
|
||||
);
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
configureStore
|
||||
};
|
|
@ -0,0 +1,57 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
|
||||
|
||||
const { executeSoon, filterByKey } = require("../../utils");
|
||||
|
||||
const PROMISE = (exports.PROMISE = "@@dispatch/promise");
|
||||
let seqIdVal = 1;
|
||||
|
||||
function seqIdGen() {
|
||||
return seqIdVal++;
|
||||
}
|
||||
|
||||
function promiseMiddleware({ dispatch, getState }) {
|
||||
return next => action => {
|
||||
if (!action || !Object.keys(action).includes(PROMISE)) {
|
||||
return next(action);
|
||||
}
|
||||
|
||||
const promiseInst = action[PROMISE];
|
||||
const seqId = seqIdGen().toString();
|
||||
|
||||
// Create a new action that doesn't have the promise field and has
|
||||
// the `seqId` field that represents the sequence id
|
||||
action = Object.assign(filterByKey(action, key => key !== PROMISE), {
|
||||
seqId
|
||||
});
|
||||
|
||||
dispatch(Object.assign({}, action, { status: "start" }));
|
||||
|
||||
// Return the promise so action creators can still compose if they want to.
|
||||
return Promise.resolve(promiseInst)
|
||||
.finally(() => new Promise(resolve => executeSoon(resolve)))
|
||||
.then(
|
||||
value => {
|
||||
dispatch(
|
||||
Object.assign({}, action, {
|
||||
status: "done",
|
||||
value: value
|
||||
})
|
||||
);
|
||||
return value;
|
||||
},
|
||||
error => {
|
||||
dispatch(
|
||||
Object.assign({}, action, {
|
||||
status: "error",
|
||||
error: error.message || error
|
||||
})
|
||||
);
|
||||
return Promise.reject(error);
|
||||
}
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
exports.promise = promiseMiddleware;
|
|
@ -0,0 +1,23 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
|
||||
|
||||
/**
|
||||
* A middleware that allows thunks (functions) to be dispatched. If
|
||||
* it's a thunk, it is called with an argument that contains
|
||||
* `dispatch`, `getState`, and any additional args passed in via the
|
||||
* middleware constructure. This allows the action to create multiple
|
||||
* actions (most likely asynchronously).
|
||||
*/
|
||||
function thunk(makeArgs) {
|
||||
return ({ dispatch, getState }) => {
|
||||
const args = { dispatch, getState };
|
||||
|
||||
return next => action => {
|
||||
return typeof action === "function"
|
||||
? action(makeArgs ? makeArgs(args, getState()) : args)
|
||||
: next(action);
|
||||
};
|
||||
};
|
||||
}
|
||||
exports.thunk = thunk;
|
|
@ -0,0 +1,60 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
|
||||
|
||||
/**
|
||||
* Returns an object with a promise and its resolve and reject function,
|
||||
* so they can be called outside of the promise callback.
|
||||
*
|
||||
* @returns {{resolve: function, reject: function, promise: Promise}}
|
||||
*/
|
||||
function defer() {
|
||||
let resolve, reject;
|
||||
const promise = new Promise((res, rej) => {
|
||||
resolve = res;
|
||||
reject = rej;
|
||||
});
|
||||
|
||||
return {
|
||||
resolve,
|
||||
reject,
|
||||
promise
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes a function and executes it on the next tick.
|
||||
*
|
||||
* @param function fn
|
||||
*/
|
||||
function executeSoon(fn) {
|
||||
setTimeout(fn, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes an object into another object,
|
||||
* filtered on its keys by the given predicate.
|
||||
*
|
||||
* @param object obj
|
||||
* @param function predicate
|
||||
* @returns object
|
||||
*/
|
||||
function filterByKey(obj, predicate) {
|
||||
return Object.keys(obj).reduce((res, key) => {
|
||||
if (predicate(key)) {
|
||||
return Object.assign(res, { [key]: obj[key] });
|
||||
}
|
||||
return res;
|
||||
}, {});
|
||||
}
|
||||
|
||||
function generateKey() {
|
||||
return `${performance.now()}`;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
defer,
|
||||
executeSoon,
|
||||
filterByKey,
|
||||
generateKey
|
||||
};
|
|
@ -0,0 +1,133 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
|
||||
|
||||
// @flow
|
||||
|
||||
import type { GripProperties, Node, Props, ReduxAction } from "./types";
|
||||
|
||||
const { loadItemProperties } = require("./utils/load-properties");
|
||||
const { getLoadedProperties, getActors } = require("./reducer");
|
||||
|
||||
type Dispatch = ReduxAction => void;
|
||||
|
||||
type ThunkArg = {
|
||||
getState: () => {},
|
||||
dispatch: Dispatch
|
||||
};
|
||||
|
||||
/**
|
||||
* This action is responsible for expanding a given node, which also means that
|
||||
* it will call the action responsible to fetch properties.
|
||||
*/
|
||||
function nodeExpand(node: Node, actor) {
|
||||
return async ({ dispatch, getState }: ThunkArg) => {
|
||||
dispatch({ type: "NODE_EXPAND", data: { node } });
|
||||
dispatch(nodeLoadProperties(node, actor));
|
||||
};
|
||||
}
|
||||
|
||||
function nodeCollapse(node: Node) {
|
||||
return {
|
||||
type: "NODE_COLLAPSE",
|
||||
data: { node }
|
||||
};
|
||||
}
|
||||
|
||||
/*
|
||||
* This action checks if we need to fetch properties, entries, prototype and
|
||||
* symbols for a given node. If we do, it will call the appropriate ObjectClient
|
||||
* functions.
|
||||
*/
|
||||
function nodeLoadProperties(node: Node, actor) {
|
||||
return async ({ dispatch, client, getState }: ThunkArg) => {
|
||||
const state = getState();
|
||||
const loadedProperties = getLoadedProperties(state);
|
||||
if (loadedProperties.has(node.path)) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const properties = await loadItemProperties(
|
||||
node,
|
||||
client.createObjectClient,
|
||||
client.createLongStringClient,
|
||||
loadedProperties
|
||||
);
|
||||
|
||||
dispatch(nodePropertiesLoaded(node, actor, properties));
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function nodePropertiesLoaded(
|
||||
node: Node,
|
||||
actor?: string,
|
||||
properties: GripProperties
|
||||
) {
|
||||
return {
|
||||
type: "NODE_PROPERTIES_LOADED",
|
||||
data: { node, actor, properties }
|
||||
};
|
||||
}
|
||||
|
||||
function closeObjectInspector() {
|
||||
return async ({ getState, client }: ThunkArg) => {
|
||||
releaseActors(getState(), client);
|
||||
};
|
||||
}
|
||||
|
||||
/*
|
||||
* This action is dispatched when the `roots` prop, provided by a consumer of
|
||||
* the ObjectInspector (inspector, console, …), is modified. It will clean the
|
||||
* internal state properties (expandedPaths, loadedProperties, …) and release
|
||||
* the actors consumed with the previous roots.
|
||||
* It takes a props argument which reflects what is passed by the upper-level
|
||||
* consumer.
|
||||
*/
|
||||
function rootsChanged(props: Props) {
|
||||
return async ({ dispatch, client, getState }: ThunkArg) => {
|
||||
releaseActors(getState(), client);
|
||||
dispatch({
|
||||
type: "ROOTS_CHANGED",
|
||||
data: props
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
function releaseActors(state, client) {
|
||||
const actors = getActors(state);
|
||||
for (const actor of actors) {
|
||||
client.releaseActor(actor);
|
||||
}
|
||||
}
|
||||
|
||||
function invokeGetter(node: Node, grip: object, getterName: string) {
|
||||
return async ({ dispatch, client, getState }: ThunkArg) => {
|
||||
try {
|
||||
const objectClient = client.createObjectClient(grip);
|
||||
const result = await objectClient.getPropertyValue(getterName);
|
||||
dispatch({
|
||||
type: "GETTER_INVOKED",
|
||||
data: {
|
||||
node,
|
||||
result
|
||||
}
|
||||
});
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
closeObjectInspector,
|
||||
invokeGetter,
|
||||
nodeExpand,
|
||||
nodeCollapse,
|
||||
nodeLoadProperties,
|
||||
nodePropertiesLoaded,
|
||||
rootsChanged
|
||||
};
|
|
@ -0,0 +1,53 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
.tree.object-inspector .node.object-node {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.tree.object-inspector .object-label,
|
||||
.tree.object-inspector .object-label * {
|
||||
color: var(--theme-highlight-blue);
|
||||
}
|
||||
|
||||
.tree.object-inspector .node .unavailable {
|
||||
color: var(--theme-comment);
|
||||
}
|
||||
|
||||
.tree.object-inspector .lessen,
|
||||
.tree.object-inspector .lessen *,
|
||||
.tree.object-inspector .lessen .object-label,
|
||||
.tree.object-inspector .lessen .object-label * {
|
||||
color: var(--theme-comment);
|
||||
}
|
||||
|
||||
.tree.object-inspector .block .object-label,
|
||||
.tree.object-inspector .block .object-label * {
|
||||
color: var(--theme-body-color);
|
||||
}
|
||||
|
||||
.tree.object-inspector .block .object-label:before {
|
||||
content: "☲ ";
|
||||
font-size: 1.1em;
|
||||
}
|
||||
|
||||
.object-inspector .object-delimiter {
|
||||
color: var(--theme-comment);
|
||||
}
|
||||
|
||||
.object-inspector .tree-node .arrow {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
/* Focused styles */
|
||||
.tree.object-inspector .tree-node.focused * {
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.tree-node.focused button.jump-definition,
|
||||
.tree-node.focused button.open-inspector,
|
||||
.tree-node.focused button.invoke-getter {
|
||||
background-color: currentColor;
|
||||
}
|
|
@ -0,0 +1,289 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
|
||||
|
||||
// @flow
|
||||
|
||||
const { Component, createFactory, createElement } = require("react");
|
||||
const { connect } = require("react-redux");
|
||||
const actions = require("../actions");
|
||||
|
||||
const selectors = require("../reducer");
|
||||
|
||||
import Components from "devtools-components";
|
||||
const Tree = createFactory(Components.Tree);
|
||||
require("./ObjectInspector.css");
|
||||
|
||||
const ObjectInspectorItem = createFactory(require("./ObjectInspectorItem"));
|
||||
|
||||
const classnames = require("classnames");
|
||||
|
||||
const Utils = require("../utils");
|
||||
const { renderRep, shouldRenderRootsInReps } = Utils;
|
||||
const {
|
||||
getChildrenWithEvaluations,
|
||||
getActor,
|
||||
getParent,
|
||||
nodeIsPrimitive,
|
||||
nodeHasGetter,
|
||||
nodeHasSetter
|
||||
} = Utils.node;
|
||||
|
||||
import type { CachedNodes, Props } from "../types";
|
||||
|
||||
type DefaultProps = {
|
||||
autoExpandAll: boolean,
|
||||
autoExpandDepth: number
|
||||
};
|
||||
|
||||
// This implements a component that renders an interactive inspector
|
||||
// for looking at JavaScript objects. It expects descriptions of
|
||||
// objects from the protocol, and will dynamically fetch children
|
||||
// properties as objects are expanded.
|
||||
//
|
||||
// If you want to inspect a single object, pass the name and the
|
||||
// protocol descriptor of it:
|
||||
//
|
||||
// ObjectInspector({
|
||||
// name: "foo",
|
||||
// desc: { writable: true, ..., { value: { actor: "1", ... }}},
|
||||
// ...
|
||||
// })
|
||||
//
|
||||
// If you want multiple top-level objects (like scopes), you can pass
|
||||
// an array of manually constructed nodes as `roots`:
|
||||
//
|
||||
// ObjectInspector({
|
||||
// roots: [{ name: ... }, ...],
|
||||
// ...
|
||||
// });
|
||||
|
||||
// There are 3 types of nodes: a simple node with a children array, an
|
||||
// object that has properties that should be children when they are
|
||||
// fetched, and a primitive value that should be displayed with no
|
||||
// children.
|
||||
|
||||
class ObjectInspector extends Component<Props> {
|
||||
static defaultProps: DefaultProps;
|
||||
constructor(props: Props) {
|
||||
super();
|
||||
this.cachedNodes = new Map();
|
||||
|
||||
const self: any = this;
|
||||
|
||||
self.getItemChildren = this.getItemChildren.bind(this);
|
||||
self.isNodeExpandable = this.isNodeExpandable.bind(this);
|
||||
self.setExpanded = this.setExpanded.bind(this);
|
||||
self.focusItem = this.focusItem.bind(this);
|
||||
self.getRoots = this.getRoots.bind(this);
|
||||
self.getNodeKey = this.getNodeKey.bind(this);
|
||||
}
|
||||
|
||||
componentWillMount() {
|
||||
this.roots = this.props.roots;
|
||||
this.focusedItem = this.props.focusedItem;
|
||||
}
|
||||
|
||||
componentWillUpdate(nextProps) {
|
||||
this.removeOutdatedNodesFromCache(nextProps);
|
||||
|
||||
if (this.roots !== nextProps.roots) {
|
||||
// Since the roots changed, we assume the properties did as well,
|
||||
// so we need to cleanup the component internal state.
|
||||
this.roots = nextProps.roots;
|
||||
this.focusedItem = nextProps.focusedItem;
|
||||
if (this.props.rootsChanged) {
|
||||
this.props.rootsChanged();
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
removeOutdatedNodesFromCache(nextProps) {
|
||||
// When the roots changes, we can wipe out everything.
|
||||
if (this.roots !== nextProps.roots) {
|
||||
this.cachedNodes.clear();
|
||||
return;
|
||||
}
|
||||
|
||||
// If there are new evaluations, we want to remove the existing cached
|
||||
// nodes from the cache.
|
||||
if (nextProps.evaluations > this.props.evaluations) {
|
||||
for (const key of nextProps.evaluations.keys()) {
|
||||
if (!this.props.evaluations.has(key)) {
|
||||
this.cachedNodes.delete(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
shouldComponentUpdate(nextProps: Props) {
|
||||
const { expandedPaths, loadedProperties, evaluations } = this.props;
|
||||
|
||||
// We should update if:
|
||||
// - there are new loaded properties
|
||||
// - OR there are new evaluations
|
||||
// - OR the expanded paths number changed, and all of them have properties
|
||||
// loaded
|
||||
// - OR the expanded paths number did not changed, but old and new sets
|
||||
// differ
|
||||
// - OR the focused node changed.
|
||||
return (
|
||||
loadedProperties.size !== nextProps.loadedProperties.size ||
|
||||
evaluations.size !== nextProps.evaluations.size ||
|
||||
(expandedPaths.size !== nextProps.expandedPaths.size &&
|
||||
[...nextProps.expandedPaths].every(path =>
|
||||
nextProps.loadedProperties.has(path)
|
||||
)) ||
|
||||
(expandedPaths.size === nextProps.expandedPaths.size &&
|
||||
[...nextProps.expandedPaths].some(key => !expandedPaths.has(key))) ||
|
||||
this.focusedItem !== nextProps.focusedItem ||
|
||||
this.roots !== nextProps.roots
|
||||
);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this.props.closeObjectInspector();
|
||||
}
|
||||
|
||||
props: Props;
|
||||
cachedNodes: CachedNodes;
|
||||
|
||||
getItemChildren(item: Node): Array<Node> | NodeContents | null {
|
||||
const { loadedProperties, evaluations } = this.props;
|
||||
const { cachedNodes } = this;
|
||||
|
||||
return getChildrenWithEvaluations({
|
||||
evaluations,
|
||||
loadedProperties,
|
||||
cachedNodes,
|
||||
item
|
||||
});
|
||||
}
|
||||
|
||||
getRoots(): Array<Node> {
|
||||
return this.props.roots;
|
||||
}
|
||||
|
||||
getNodeKey(item: Node): string {
|
||||
return item.path && typeof item.path.toString === "function"
|
||||
? item.path.toString()
|
||||
: JSON.stringify(item);
|
||||
}
|
||||
|
||||
isNodeExpandable(item: Node): boolean {
|
||||
if (nodeIsPrimitive(item)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (nodeHasSetter(item) || nodeHasGetter(item)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
setExpanded(item: Node, expand: boolean) {
|
||||
if (!this.isNodeExpandable(item)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const {
|
||||
nodeExpand,
|
||||
nodeCollapse,
|
||||
recordTelemetryEvent,
|
||||
roots
|
||||
} = this.props;
|
||||
|
||||
if (expand === true) {
|
||||
const actor = getActor(item, roots);
|
||||
nodeExpand(item, actor);
|
||||
if (recordTelemetryEvent) {
|
||||
recordTelemetryEvent("object_expanded");
|
||||
}
|
||||
} else {
|
||||
nodeCollapse(item);
|
||||
}
|
||||
}
|
||||
|
||||
focusItem(item: Node) {
|
||||
const { focusable = true, onFocus } = this.props;
|
||||
|
||||
if (focusable && this.focusedItem !== item) {
|
||||
this.focusedItem = item;
|
||||
this.forceUpdate();
|
||||
|
||||
if (onFocus) {
|
||||
onFocus(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
autoExpandAll = true,
|
||||
autoExpandDepth = 1,
|
||||
focusable = true,
|
||||
disableWrap = false,
|
||||
expandedPaths,
|
||||
inline
|
||||
} = this.props;
|
||||
|
||||
return Tree({
|
||||
className: classnames({
|
||||
inline,
|
||||
nowrap: disableWrap,
|
||||
"object-inspector": true
|
||||
}),
|
||||
|
||||
autoExpandAll,
|
||||
autoExpandDepth,
|
||||
|
||||
isExpanded: item => expandedPaths && expandedPaths.has(item.path),
|
||||
isExpandable: this.isNodeExpandable,
|
||||
focused: this.focusedItem,
|
||||
|
||||
getRoots: this.getRoots,
|
||||
getParent,
|
||||
getChildren: this.getItemChildren,
|
||||
getKey: this.getNodeKey,
|
||||
|
||||
onExpand: item => this.setExpanded(item, true),
|
||||
onCollapse: item => this.setExpanded(item, false),
|
||||
onFocus: focusable ? this.focusItem : null,
|
||||
|
||||
renderItem: (item, depth, focused, arrow, expanded) =>
|
||||
ObjectInspectorItem({
|
||||
...this.props,
|
||||
item,
|
||||
depth,
|
||||
focused,
|
||||
arrow,
|
||||
expanded,
|
||||
setExpanded: this.setExpanded
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function mapStateToProps(state, props) {
|
||||
return {
|
||||
expandedPaths: selectors.getExpandedPaths(state),
|
||||
loadedProperties: selectors.getLoadedProperties(state),
|
||||
evaluations: selectors.getEvaluations(state)
|
||||
};
|
||||
}
|
||||
|
||||
const OI = connect(
|
||||
mapStateToProps,
|
||||
actions
|
||||
)(ObjectInspector);
|
||||
|
||||
module.exports = (props: Props) => {
|
||||
const { roots } = props;
|
||||
if (shouldRenderRootsInReps(roots)) {
|
||||
return renderRep(roots[0], props);
|
||||
}
|
||||
|
||||
return createElement(OI, props);
|
||||
};
|
|
@ -0,0 +1,308 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
|
||||
|
||||
// @flow
|
||||
|
||||
const { Component } = require("react");
|
||||
const dom = require("react-dom-factories");
|
||||
|
||||
import Services from "devtools-services";
|
||||
const { appinfo } = Services;
|
||||
const isMacOS = appinfo.OS === "Darwin";
|
||||
|
||||
const classnames = require("classnames");
|
||||
const { MODE } = require("../../reps/constants");
|
||||
|
||||
const Utils = require("../utils");
|
||||
|
||||
const {
|
||||
getValue,
|
||||
nodeHasAccessors,
|
||||
nodeHasProperties,
|
||||
nodeIsBlock,
|
||||
nodeIsDefaultProperties,
|
||||
nodeIsFunction,
|
||||
nodeIsGetter,
|
||||
nodeIsMapEntry,
|
||||
nodeIsMissingArguments,
|
||||
nodeIsOptimizedOut,
|
||||
nodeIsPrimitive,
|
||||
nodeIsPrototype,
|
||||
nodeIsSetter,
|
||||
nodeIsUninitializedBinding,
|
||||
nodeIsUnmappedBinding,
|
||||
nodeIsUnscopedBinding,
|
||||
nodeIsWindow,
|
||||
nodeIsLongString,
|
||||
nodeHasFullText,
|
||||
nodeHasGetter,
|
||||
getNonPrototypeParentGripValue
|
||||
} = Utils.node;
|
||||
|
||||
type Props = {
|
||||
item: Node,
|
||||
depth: number,
|
||||
expanded: boolean,
|
||||
focused: boolean,
|
||||
arrow: ReactElement,
|
||||
setExpanded: (item: Node, expanded: boolean) => void,
|
||||
mode: Mode,
|
||||
dimTopLevelWindow: boolean,
|
||||
invokeGetter: () => void,
|
||||
onDoubleClick: ?(
|
||||
item: Node,
|
||||
options: {
|
||||
depth: number,
|
||||
focused: boolean,
|
||||
expanded: boolean
|
||||
}
|
||||
) => any,
|
||||
onCmdCtrlClick: ?(
|
||||
item: Node,
|
||||
options: {
|
||||
depth: number,
|
||||
event: SyntheticEvent,
|
||||
focused: boolean,
|
||||
expanded: boolean
|
||||
}
|
||||
) => any,
|
||||
onLabelClick: ?(
|
||||
item: Node,
|
||||
options: {
|
||||
depth: number,
|
||||
focused: boolean,
|
||||
expanded: boolean,
|
||||
setExpanded: (Node, boolean) => any
|
||||
}
|
||||
) => any
|
||||
};
|
||||
|
||||
class ObjectInspectorItem extends Component<Props> {
|
||||
// eslint-disable-next-line complexity
|
||||
getLabelAndValue(): {
|
||||
value?: string | Element,
|
||||
label?: string
|
||||
} {
|
||||
const { item, depth, expanded, mode } = this.props;
|
||||
|
||||
const label = item.name;
|
||||
const isPrimitive = nodeIsPrimitive(item);
|
||||
|
||||
if (nodeIsOptimizedOut(item)) {
|
||||
return {
|
||||
label,
|
||||
value: dom.span({ className: "unavailable" }, "(optimized away)")
|
||||
};
|
||||
}
|
||||
|
||||
if (nodeIsUninitializedBinding(item)) {
|
||||
return {
|
||||
label,
|
||||
value: dom.span({ className: "unavailable" }, "(uninitialized)")
|
||||
};
|
||||
}
|
||||
|
||||
if (nodeIsUnmappedBinding(item)) {
|
||||
return {
|
||||
label,
|
||||
value: dom.span({ className: "unavailable" }, "(unmapped)")
|
||||
};
|
||||
}
|
||||
|
||||
if (nodeIsUnscopedBinding(item)) {
|
||||
return {
|
||||
label,
|
||||
value: dom.span({ className: "unavailable" }, "(unscoped)")
|
||||
};
|
||||
}
|
||||
|
||||
const itemValue = getValue(item);
|
||||
const unavailable =
|
||||
isPrimitive &&
|
||||
itemValue &&
|
||||
itemValue.hasOwnProperty &&
|
||||
itemValue.hasOwnProperty("unavailable");
|
||||
|
||||
if (nodeIsMissingArguments(item) || unavailable) {
|
||||
return {
|
||||
label,
|
||||
value: dom.span({ className: "unavailable" }, "(unavailable)")
|
||||
};
|
||||
}
|
||||
|
||||
if (
|
||||
nodeIsFunction(item) &&
|
||||
!nodeIsGetter(item) &&
|
||||
!nodeIsSetter(item) &&
|
||||
(mode === MODE.TINY || !mode)
|
||||
) {
|
||||
return {
|
||||
label: Utils.renderRep(item, {
|
||||
...this.props,
|
||||
functionName: label
|
||||
})
|
||||
};
|
||||
}
|
||||
|
||||
if (
|
||||
nodeHasProperties(item) ||
|
||||
nodeHasAccessors(item) ||
|
||||
nodeIsMapEntry(item) ||
|
||||
nodeIsLongString(item) ||
|
||||
isPrimitive
|
||||
) {
|
||||
const repProps = { ...this.props };
|
||||
if (depth > 0) {
|
||||
repProps.mode = mode === MODE.LONG ? MODE.SHORT : MODE.TINY;
|
||||
}
|
||||
if (expanded) {
|
||||
repProps.mode = MODE.TINY;
|
||||
}
|
||||
|
||||
if (nodeIsLongString(item)) {
|
||||
repProps.member = {
|
||||
open: nodeHasFullText(item) && expanded
|
||||
};
|
||||
}
|
||||
|
||||
if (nodeHasGetter(item)) {
|
||||
const parentGrip = getNonPrototypeParentGripValue(item);
|
||||
if (parentGrip) {
|
||||
Object.assign(repProps, {
|
||||
onInvokeGetterButtonClick: () =>
|
||||
this.props.invokeGetter(item, parentGrip, item.name)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
label,
|
||||
value: Utils.renderRep(item, repProps)
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
label
|
||||
};
|
||||
}
|
||||
|
||||
getTreeItemProps(): Object {
|
||||
const {
|
||||
item,
|
||||
depth,
|
||||
focused,
|
||||
expanded,
|
||||
onCmdCtrlClick,
|
||||
onDoubleClick,
|
||||
dimTopLevelWindow
|
||||
} = this.props;
|
||||
|
||||
const parentElementProps: Object = {
|
||||
className: classnames("node object-node", {
|
||||
focused,
|
||||
lessen:
|
||||
!expanded &&
|
||||
(nodeIsDefaultProperties(item) ||
|
||||
nodeIsPrototype(item) ||
|
||||
nodeIsGetter(item) ||
|
||||
nodeIsSetter(item) ||
|
||||
(dimTopLevelWindow === true && nodeIsWindow(item) && depth === 0)),
|
||||
block: nodeIsBlock(item)
|
||||
}),
|
||||
onClick: e => {
|
||||
if (
|
||||
onCmdCtrlClick &&
|
||||
((isMacOS && e.metaKey) || (!isMacOS && e.ctrlKey))
|
||||
) {
|
||||
onCmdCtrlClick(item, {
|
||||
depth,
|
||||
event: e,
|
||||
focused,
|
||||
expanded
|
||||
});
|
||||
e.stopPropagation();
|
||||
return;
|
||||
}
|
||||
|
||||
// If this click happened because the user selected some text, bail out.
|
||||
// Note that if the user selected some text before and then clicks here,
|
||||
// the previously selected text will be first unselected, unless the
|
||||
// user clicked on the arrow itself. Indeed because the arrow is an
|
||||
// image, clicking on it does not remove any existing text selection.
|
||||
// So we need to also check if the arrow was clicked.
|
||||
if (
|
||||
Utils.selection.documentHasSelection() &&
|
||||
!(e.target && e.target.matches && e.target.matches(".arrow"))
|
||||
) {
|
||||
e.stopPropagation();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if (onDoubleClick) {
|
||||
parentElementProps.onDoubleClick = e => {
|
||||
e.stopPropagation();
|
||||
onDoubleClick(item, {
|
||||
depth,
|
||||
focused,
|
||||
expanded
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
return parentElementProps;
|
||||
}
|
||||
|
||||
renderLabel(label: string) {
|
||||
if (label === null || typeof label === "undefined") {
|
||||
return null;
|
||||
}
|
||||
|
||||
const { item, depth, focused, expanded, onLabelClick } = this.props;
|
||||
return dom.span(
|
||||
{
|
||||
className: "object-label",
|
||||
onClick: onLabelClick
|
||||
? event => {
|
||||
event.stopPropagation();
|
||||
|
||||
// If the user selected text, bail out.
|
||||
if (Utils.selection.documentHasSelection()) {
|
||||
return;
|
||||
}
|
||||
|
||||
onLabelClick(item, {
|
||||
depth,
|
||||
focused,
|
||||
expanded,
|
||||
setExpanded: this.props.setExpanded
|
||||
});
|
||||
}
|
||||
: undefined
|
||||
},
|
||||
label
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
const { arrow } = this.props;
|
||||
|
||||
const { label, value } = this.getLabelAndValue();
|
||||
const labelElement = this.renderLabel(label);
|
||||
const delimiter =
|
||||
value && labelElement
|
||||
? dom.span({ className: "object-delimiter" }, ": ")
|
||||
: null;
|
||||
|
||||
return dom.div(
|
||||
this.getTreeItemProps(),
|
||||
arrow,
|
||||
labelElement,
|
||||
delimiter,
|
||||
value
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = ObjectInspectorItem;
|
|
@ -0,0 +1,9 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
|
||||
|
||||
const ObjectInspector = require("./components/ObjectInspector");
|
||||
const utils = require("./utils");
|
||||
const reducer = require("./reducer");
|
||||
|
||||
module.exports = { ObjectInspector, utils, reducer };
|
|
@ -0,0 +1,117 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
|
||||
// @flow
|
||||
|
||||
import type { ReduxAction, State } from "./types";
|
||||
|
||||
function initialState() {
|
||||
return {
|
||||
expandedPaths: new Set(),
|
||||
loadedProperties: new Map(),
|
||||
evaluations: new Map(),
|
||||
actors: new Set()
|
||||
};
|
||||
}
|
||||
|
||||
function reducer(
|
||||
state: State = initialState(),
|
||||
action: ReduxAction = {}
|
||||
): State {
|
||||
const { type, data } = action;
|
||||
|
||||
const cloneState = overrides => ({ ...state, ...overrides });
|
||||
|
||||
if (type === "NODE_EXPAND") {
|
||||
return cloneState({
|
||||
expandedPaths: new Set(state.expandedPaths).add(data.node.path)
|
||||
});
|
||||
}
|
||||
|
||||
if (type === "NODE_COLLAPSE") {
|
||||
const expandedPaths = new Set(state.expandedPaths);
|
||||
expandedPaths.delete(data.node.path);
|
||||
return cloneState({ expandedPaths });
|
||||
}
|
||||
|
||||
if (type === "NODE_PROPERTIES_LOADED") {
|
||||
return cloneState({
|
||||
actors: data.actor
|
||||
? new Set(state.actors || []).add(data.actor)
|
||||
: state.actors,
|
||||
loadedProperties: new Map(state.loadedProperties).set(
|
||||
data.node.path,
|
||||
action.data.properties
|
||||
)
|
||||
});
|
||||
}
|
||||
|
||||
if (type === "ROOTS_CHANGED") {
|
||||
return cloneState();
|
||||
}
|
||||
|
||||
if (type === "GETTER_INVOKED") {
|
||||
return cloneState({
|
||||
actors: data.actor
|
||||
? new Set(state.actors || []).add(data.result.from)
|
||||
: state.actors,
|
||||
evaluations: new Map(state.evaluations).set(data.node.path, {
|
||||
getterValue:
|
||||
data.result &&
|
||||
data.result.value &&
|
||||
(data.result.value.return || data.result.value.throw)
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
// NOTE: we clear the state on resume because otherwise the scopes pane
|
||||
// would be out of date. Bug 1514760
|
||||
if (type === "RESUME" || type == "NAVIGATE") {
|
||||
return initialState();
|
||||
}
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
function getObjectInspectorState(state) {
|
||||
return state.objectInspector;
|
||||
}
|
||||
|
||||
function getExpandedPaths(state) {
|
||||
return getObjectInspectorState(state).expandedPaths;
|
||||
}
|
||||
|
||||
function getExpandedPathKeys(state) {
|
||||
return [...getExpandedPaths(state).keys()];
|
||||
}
|
||||
|
||||
function getActors(state) {
|
||||
return getObjectInspectorState(state).actors;
|
||||
}
|
||||
|
||||
function getLoadedProperties(state) {
|
||||
return getObjectInspectorState(state).loadedProperties;
|
||||
}
|
||||
|
||||
function getLoadedPropertyKeys(state) {
|
||||
return [...getLoadedProperties(state).keys()];
|
||||
}
|
||||
|
||||
function getEvaluations(state) {
|
||||
return getObjectInspectorState(state).evaluations;
|
||||
}
|
||||
|
||||
const selectors = {
|
||||
getActors,
|
||||
getEvaluations,
|
||||
getExpandedPathKeys,
|
||||
getExpandedPaths,
|
||||
getLoadedProperties,
|
||||
getLoadedPropertyKeys
|
||||
};
|
||||
|
||||
Object.defineProperty(module.exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
module.exports = selectors;
|
||||
module.exports.default = reducer;
|
|
@ -0,0 +1,64 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
|
||||
|
||||
const stubs = new Map();
|
||||
|
||||
stubs.set("proto-properties-symbols", {
|
||||
ownProperties: {
|
||||
a: {
|
||||
configurable: true,
|
||||
enumerable: true,
|
||||
writable: true,
|
||||
value: 1
|
||||
}
|
||||
},
|
||||
from: "server2.conn13.child19/propertyIterator160",
|
||||
prototype: {
|
||||
type: "object",
|
||||
actor: "server2.conn13.child19/obj162",
|
||||
class: "Object",
|
||||
extensible: true,
|
||||
frozen: false,
|
||||
sealed: false,
|
||||
ownPropertyLength: 15,
|
||||
preview: {
|
||||
kind: "Object",
|
||||
ownProperties: {},
|
||||
ownSymbols: [],
|
||||
ownPropertiesLength: 15,
|
||||
ownSymbolsLength: 0,
|
||||
safeGetterValues: {}
|
||||
}
|
||||
},
|
||||
ownSymbols: [
|
||||
{
|
||||
name: "Symbol()",
|
||||
descriptor: {
|
||||
configurable: true,
|
||||
enumerable: true,
|
||||
writable: true,
|
||||
value: "hello"
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
stubs.set("longs-string-safe-getter", {
|
||||
ownProperties: {
|
||||
baseVal: {
|
||||
getterValue: {
|
||||
type: "longString",
|
||||
initial: "data:image/png;base64,initial",
|
||||
length: 95080,
|
||||
actor: "server1.conn1.child1/longString28"
|
||||
},
|
||||
getterPrototypeLevel: 1,
|
||||
enumerable: true,
|
||||
writable: true
|
||||
}
|
||||
},
|
||||
from: "server1.conn1.child1/propertyIterator30"
|
||||
});
|
||||
|
||||
module.exports = stubs;
|
|
@ -0,0 +1,154 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
|
||||
|
||||
const stubs = new Map();
|
||||
|
||||
stubs.set("properties", {
|
||||
from: "server2.conn14.child18/obj30",
|
||||
prototype: {
|
||||
type: "object",
|
||||
actor: "server2.conn14.child18/obj31",
|
||||
class: "Object",
|
||||
extensible: true,
|
||||
frozen: false,
|
||||
sealed: false,
|
||||
ownPropertyLength: 11,
|
||||
preview: {
|
||||
kind: "Object",
|
||||
ownProperties: {},
|
||||
ownSymbols: [],
|
||||
ownPropertiesLength: 11,
|
||||
ownSymbolsLength: 2,
|
||||
safeGetterValues: {}
|
||||
}
|
||||
},
|
||||
ownProperties: {},
|
||||
ownSymbols: [],
|
||||
safeGetterValues: {
|
||||
size: {
|
||||
getterValue: 2,
|
||||
getterPrototypeLevel: 2,
|
||||
enumerable: false,
|
||||
writable: true
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
stubs.set("11-entries", {
|
||||
ownProperties: {
|
||||
"0": {
|
||||
enumerable: true,
|
||||
value: {
|
||||
type: "mapEntry",
|
||||
preview: {
|
||||
key: "key-0",
|
||||
value: "value-0"
|
||||
}
|
||||
}
|
||||
},
|
||||
"1": {
|
||||
enumerable: true,
|
||||
value: {
|
||||
type: "mapEntry",
|
||||
preview: {
|
||||
key: "key-1",
|
||||
value: "value-1"
|
||||
}
|
||||
}
|
||||
},
|
||||
"2": {
|
||||
enumerable: true,
|
||||
value: {
|
||||
type: "mapEntry",
|
||||
preview: {
|
||||
key: "key-2",
|
||||
value: "value-2"
|
||||
}
|
||||
}
|
||||
},
|
||||
"3": {
|
||||
enumerable: true,
|
||||
value: {
|
||||
type: "mapEntry",
|
||||
preview: {
|
||||
key: "key-3",
|
||||
value: "value-3"
|
||||
}
|
||||
}
|
||||
},
|
||||
"4": {
|
||||
enumerable: true,
|
||||
value: {
|
||||
type: "mapEntry",
|
||||
preview: {
|
||||
key: "key-4",
|
||||
value: "value-4"
|
||||
}
|
||||
}
|
||||
},
|
||||
"5": {
|
||||
enumerable: true,
|
||||
value: {
|
||||
type: "mapEntry",
|
||||
preview: {
|
||||
key: "key-5",
|
||||
value: "value-5"
|
||||
}
|
||||
}
|
||||
},
|
||||
"6": {
|
||||
enumerable: true,
|
||||
value: {
|
||||
type: "mapEntry",
|
||||
preview: {
|
||||
key: "key-6",
|
||||
value: "value-6"
|
||||
}
|
||||
}
|
||||
},
|
||||
"7": {
|
||||
enumerable: true,
|
||||
value: {
|
||||
type: "mapEntry",
|
||||
preview: {
|
||||
key: "key-7",
|
||||
value: "value-7"
|
||||
}
|
||||
}
|
||||
},
|
||||
"8": {
|
||||
enumerable: true,
|
||||
value: {
|
||||
type: "mapEntry",
|
||||
preview: {
|
||||
key: "key-8",
|
||||
value: "value-8"
|
||||
}
|
||||
}
|
||||
},
|
||||
"9": {
|
||||
enumerable: true,
|
||||
value: {
|
||||
type: "mapEntry",
|
||||
preview: {
|
||||
key: "key-9",
|
||||
value: "value-9"
|
||||
}
|
||||
}
|
||||
},
|
||||
"10": {
|
||||
enumerable: true,
|
||||
value: {
|
||||
type: "mapEntry",
|
||||
preview: {
|
||||
key: "key-10",
|
||||
value: "value-10"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
from: "server4.conn4.child19/propertyIterator54"
|
||||
});
|
||||
|
||||
module.exports = stubs;
|
|
@ -0,0 +1,784 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
|
||||
|
||||
const stubs = new Map();
|
||||
|
||||
stubs.set("performance", {
|
||||
from: "server2.conn4.child1/obj30",
|
||||
prototype: {
|
||||
type: "object",
|
||||
actor: "server2.conn4.child1/obj33",
|
||||
class: "PerformancePrototype",
|
||||
extensible: true,
|
||||
frozen: false,
|
||||
sealed: false,
|
||||
ownPropertyLength: 16,
|
||||
preview: {
|
||||
kind: "Object",
|
||||
ownProperties: {
|
||||
now: {
|
||||
configurable: true,
|
||||
enumerable: true,
|
||||
writable: true,
|
||||
value: {
|
||||
type: "object",
|
||||
actor: "server2.conn4.child1/obj34",
|
||||
class: "Function",
|
||||
extensible: true,
|
||||
frozen: false,
|
||||
sealed: false,
|
||||
name: "now",
|
||||
displayName: "now"
|
||||
}
|
||||
},
|
||||
getEntries: {
|
||||
configurable: true,
|
||||
enumerable: true,
|
||||
writable: true,
|
||||
value: {
|
||||
type: "object",
|
||||
actor: "server2.conn4.child1/obj35",
|
||||
class: "Function",
|
||||
extensible: true,
|
||||
frozen: false,
|
||||
sealed: false,
|
||||
name: "getEntries",
|
||||
displayName: "getEntries"
|
||||
}
|
||||
},
|
||||
getEntriesByType: {
|
||||
configurable: true,
|
||||
enumerable: true,
|
||||
writable: true,
|
||||
value: {
|
||||
type: "object",
|
||||
actor: "server2.conn4.child1/obj36",
|
||||
class: "Function",
|
||||
extensible: true,
|
||||
frozen: false,
|
||||
sealed: false,
|
||||
name: "getEntriesByType",
|
||||
displayName: "getEntriesByType"
|
||||
}
|
||||
},
|
||||
getEntriesByName: {
|
||||
configurable: true,
|
||||
enumerable: true,
|
||||
writable: true,
|
||||
value: {
|
||||
type: "object",
|
||||
actor: "server2.conn4.child1/obj37",
|
||||
class: "Function",
|
||||
extensible: true,
|
||||
frozen: false,
|
||||
sealed: false,
|
||||
name: "getEntriesByName",
|
||||
displayName: "getEntriesByName"
|
||||
}
|
||||
},
|
||||
clearResourceTimings: {
|
||||
configurable: true,
|
||||
enumerable: true,
|
||||
writable: true,
|
||||
value: {
|
||||
type: "object",
|
||||
actor: "server2.conn4.child1/obj38",
|
||||
class: "Function",
|
||||
extensible: true,
|
||||
frozen: false,
|
||||
sealed: false,
|
||||
name: "clearResourceTimings",
|
||||
displayName: "clearResourceTimings"
|
||||
}
|
||||
},
|
||||
setResourceTimingBufferSize: {
|
||||
configurable: true,
|
||||
enumerable: true,
|
||||
writable: true,
|
||||
value: {
|
||||
type: "object",
|
||||
actor: "server2.conn4.child1/obj39",
|
||||
class: "Function",
|
||||
extensible: true,
|
||||
frozen: false,
|
||||
sealed: false,
|
||||
name: "setResourceTimingBufferSize",
|
||||
displayName: "setResourceTimingBufferSize"
|
||||
}
|
||||
},
|
||||
mark: {
|
||||
configurable: true,
|
||||
enumerable: true,
|
||||
writable: true,
|
||||
value: {
|
||||
type: "object",
|
||||
actor: "server2.conn4.child1/obj40",
|
||||
class: "Function",
|
||||
extensible: true,
|
||||
frozen: false,
|
||||
sealed: false,
|
||||
name: "mark",
|
||||
displayName: "mark"
|
||||
}
|
||||
},
|
||||
clearMarks: {
|
||||
configurable: true,
|
||||
enumerable: true,
|
||||
writable: true,
|
||||
value: {
|
||||
type: "object",
|
||||
actor: "server2.conn4.child1/obj41",
|
||||
class: "Function",
|
||||
extensible: true,
|
||||
frozen: false,
|
||||
sealed: false,
|
||||
name: "clearMarks",
|
||||
displayName: "clearMarks"
|
||||
}
|
||||
},
|
||||
measure: {
|
||||
configurable: true,
|
||||
enumerable: true,
|
||||
writable: true,
|
||||
value: {
|
||||
type: "object",
|
||||
actor: "server2.conn4.child1/obj42",
|
||||
class: "Function",
|
||||
extensible: true,
|
||||
frozen: false,
|
||||
sealed: false,
|
||||
name: "measure",
|
||||
displayName: "measure"
|
||||
}
|
||||
},
|
||||
clearMeasures: {
|
||||
configurable: true,
|
||||
enumerable: true,
|
||||
writable: true,
|
||||
value: {
|
||||
type: "object",
|
||||
actor: "server2.conn4.child1/obj43",
|
||||
class: "Function",
|
||||
extensible: true,
|
||||
frozen: false,
|
||||
sealed: false,
|
||||
name: "clearMeasures",
|
||||
displayName: "clearMeasures"
|
||||
}
|
||||
}
|
||||
},
|
||||
ownPropertiesLength: 16
|
||||
}
|
||||
},
|
||||
ownProperties: {
|
||||
userTimingJsNow: {
|
||||
configurable: true,
|
||||
enumerable: true,
|
||||
writable: true,
|
||||
value: false
|
||||
},
|
||||
userTimingJsNowPrefixed: {
|
||||
configurable: true,
|
||||
enumerable: true,
|
||||
writable: true,
|
||||
value: false
|
||||
},
|
||||
userTimingJsUserTiming: {
|
||||
configurable: true,
|
||||
enumerable: true,
|
||||
writable: true,
|
||||
value: false
|
||||
},
|
||||
userTimingJsUserTimingPrefixed: {
|
||||
configurable: true,
|
||||
enumerable: true,
|
||||
writable: true,
|
||||
value: false
|
||||
},
|
||||
userTimingJsPerformanceTimeline: {
|
||||
configurable: true,
|
||||
enumerable: true,
|
||||
writable: true,
|
||||
value: false
|
||||
},
|
||||
userTimingJsPerformanceTimelinePrefixed: {
|
||||
configurable: true,
|
||||
enumerable: true,
|
||||
writable: true,
|
||||
value: false
|
||||
},
|
||||
timeOrigin: {
|
||||
enumerable: true,
|
||||
writable: true,
|
||||
value: 1500971976372.9033
|
||||
},
|
||||
timing: {
|
||||
enumerable: true,
|
||||
writable: true,
|
||||
value: {
|
||||
type: "object",
|
||||
actor: "server2.conn4.child1/obj44",
|
||||
class: "PerformanceTiming",
|
||||
extensible: true,
|
||||
frozen: false,
|
||||
sealed: false,
|
||||
ownPropertyLength: 0,
|
||||
preview: {
|
||||
kind: "Object",
|
||||
ownProperties: {},
|
||||
ownPropertiesLength: 0,
|
||||
safeGetterValues: {
|
||||
navigationStart: {
|
||||
getterValue: 1500971976373,
|
||||
getterPrototypeLevel: 1,
|
||||
enumerable: true,
|
||||
writable: true
|
||||
},
|
||||
unloadEventStart: {
|
||||
getterValue: 0,
|
||||
getterPrototypeLevel: 1,
|
||||
enumerable: true,
|
||||
writable: true
|
||||
},
|
||||
unloadEventEnd: {
|
||||
getterValue: 0,
|
||||
getterPrototypeLevel: 1,
|
||||
enumerable: true,
|
||||
writable: true
|
||||
},
|
||||
redirectStart: {
|
||||
getterValue: 0,
|
||||
getterPrototypeLevel: 1,
|
||||
enumerable: true,
|
||||
writable: true
|
||||
},
|
||||
redirectEnd: {
|
||||
getterValue: 0,
|
||||
getterPrototypeLevel: 1,
|
||||
enumerable: true,
|
||||
writable: true
|
||||
},
|
||||
fetchStart: {
|
||||
getterValue: 1500971982226,
|
||||
getterPrototypeLevel: 1,
|
||||
enumerable: true,
|
||||
writable: true
|
||||
},
|
||||
domainLookupStart: {
|
||||
getterValue: 1500971982251,
|
||||
getterPrototypeLevel: 1,
|
||||
enumerable: true,
|
||||
writable: true
|
||||
},
|
||||
domainLookupEnd: {
|
||||
getterValue: 1500971982255,
|
||||
getterPrototypeLevel: 1,
|
||||
enumerable: true,
|
||||
writable: true
|
||||
},
|
||||
connectStart: {
|
||||
getterValue: 1500971982255,
|
||||
getterPrototypeLevel: 1,
|
||||
enumerable: true,
|
||||
writable: true
|
||||
},
|
||||
connectEnd: {
|
||||
getterValue: 1500971982638,
|
||||
getterPrototypeLevel: 1,
|
||||
enumerable: true,
|
||||
writable: true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
navigation: {
|
||||
enumerable: true,
|
||||
writable: true,
|
||||
value: {
|
||||
type: "object",
|
||||
actor: "server2.conn4.child1/obj45",
|
||||
class: "PerformanceNavigation",
|
||||
extensible: true,
|
||||
frozen: false,
|
||||
sealed: false,
|
||||
ownPropertyLength: 0,
|
||||
preview: {
|
||||
kind: "Object",
|
||||
ownProperties: {},
|
||||
ownPropertiesLength: 0,
|
||||
safeGetterValues: {
|
||||
type: {
|
||||
getterValue: 0,
|
||||
getterPrototypeLevel: 1,
|
||||
enumerable: true,
|
||||
writable: true
|
||||
},
|
||||
redirectCount: {
|
||||
getterValue: 0,
|
||||
getterPrototypeLevel: 1,
|
||||
enumerable: true,
|
||||
writable: true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
onresourcetimingbufferfull: {
|
||||
enumerable: true,
|
||||
writable: true,
|
||||
value: {
|
||||
type: "null"
|
||||
}
|
||||
}
|
||||
},
|
||||
safeGetterValues: {
|
||||
timeOrigin: {
|
||||
getterValue: 1500971976372.9033,
|
||||
getterPrototypeLevel: 1,
|
||||
enumerable: true,
|
||||
writable: true
|
||||
},
|
||||
timing: {
|
||||
getterValue: {
|
||||
type: "object",
|
||||
actor: "server2.conn4.child1/obj44",
|
||||
class: "PerformanceTiming",
|
||||
extensible: true,
|
||||
frozen: false,
|
||||
sealed: false,
|
||||
ownPropertyLength: 0,
|
||||
preview: {
|
||||
kind: "Object",
|
||||
ownProperties: {},
|
||||
ownPropertiesLength: 0,
|
||||
safeGetterValues: {
|
||||
navigationStart: {
|
||||
getterValue: 1500971976373,
|
||||
getterPrototypeLevel: 1,
|
||||
enumerable: true,
|
||||
writable: true
|
||||
},
|
||||
unloadEventStart: {
|
||||
getterValue: 0,
|
||||
getterPrototypeLevel: 1,
|
||||
enumerable: true,
|
||||
writable: true
|
||||
},
|
||||
unloadEventEnd: {
|
||||
getterValue: 0,
|
||||
getterPrototypeLevel: 1,
|
||||
enumerable: true,
|
||||
writable: true
|
||||
},
|
||||
redirectStart: {
|
||||
getterValue: 0,
|
||||
getterPrototypeLevel: 1,
|
||||
enumerable: true,
|
||||
writable: true
|
||||
},
|
||||
redirectEnd: {
|
||||
getterValue: 0,
|
||||
getterPrototypeLevel: 1,
|
||||
enumerable: true,
|
||||
writable: true
|
||||
},
|
||||
fetchStart: {
|
||||
getterValue: 1500971982226,
|
||||
getterPrototypeLevel: 1,
|
||||
enumerable: true,
|
||||
writable: true
|
||||
},
|
||||
domainLookupStart: {
|
||||
getterValue: 1500971982251,
|
||||
getterPrototypeLevel: 1,
|
||||
enumerable: true,
|
||||
writable: true
|
||||
},
|
||||
domainLookupEnd: {
|
||||
getterValue: 1500971982255,
|
||||
getterPrototypeLevel: 1,
|
||||
enumerable: true,
|
||||
writable: true
|
||||
},
|
||||
connectStart: {
|
||||
getterValue: 1500971982255,
|
||||
getterPrototypeLevel: 1,
|
||||
enumerable: true,
|
||||
writable: true
|
||||
},
|
||||
connectEnd: {
|
||||
getterValue: 1500971982638,
|
||||
getterPrototypeLevel: 1,
|
||||
enumerable: true,
|
||||
writable: true
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
getterPrototypeLevel: 1,
|
||||
enumerable: true,
|
||||
writable: true
|
||||
},
|
||||
navigation: {
|
||||
getterValue: {
|
||||
type: "object",
|
||||
actor: "server2.conn4.child1/obj45",
|
||||
class: "PerformanceNavigation",
|
||||
extensible: true,
|
||||
frozen: false,
|
||||
sealed: false,
|
||||
ownPropertyLength: 0,
|
||||
preview: {
|
||||
kind: "Object",
|
||||
ownProperties: {},
|
||||
ownPropertiesLength: 0,
|
||||
safeGetterValues: {
|
||||
type: {
|
||||
getterValue: 0,
|
||||
getterPrototypeLevel: 1,
|
||||
enumerable: true,
|
||||
writable: true
|
||||
},
|
||||
redirectCount: {
|
||||
getterValue: 0,
|
||||
getterPrototypeLevel: 1,
|
||||
enumerable: true,
|
||||
writable: true
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
getterPrototypeLevel: 1,
|
||||
enumerable: true,
|
||||
writable: true
|
||||
},
|
||||
onresourcetimingbufferfull: {
|
||||
getterValue: {
|
||||
type: "null"
|
||||
},
|
||||
getterPrototypeLevel: 1,
|
||||
enumerable: true,
|
||||
writable: true
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
stubs.set("timing", {
|
||||
from: "server1.conn1.child1/obj31",
|
||||
prototype: {
|
||||
type: "object",
|
||||
actor: "server1.conn1.child1/obj32",
|
||||
class: "PerformanceTimingPrototype",
|
||||
extensible: true,
|
||||
frozen: false,
|
||||
sealed: false,
|
||||
ownPropertyLength: 23,
|
||||
preview: {
|
||||
kind: "Object",
|
||||
ownProperties: {
|
||||
toJSON: {
|
||||
configurable: true,
|
||||
enumerable: true,
|
||||
writable: true,
|
||||
value: {
|
||||
type: "object",
|
||||
actor: "server1.conn1.child1/obj33",
|
||||
class: "Function",
|
||||
extensible: true,
|
||||
frozen: false,
|
||||
sealed: false,
|
||||
name: "toJSON",
|
||||
displayName: "toJSON"
|
||||
}
|
||||
},
|
||||
navigationStart: {
|
||||
configurable: true,
|
||||
enumerable: true,
|
||||
get: {
|
||||
type: "object",
|
||||
actor: "server1.conn1.child1/obj34",
|
||||
class: "Function",
|
||||
extensible: true,
|
||||
frozen: false,
|
||||
sealed: false,
|
||||
name: "get navigationStart",
|
||||
displayName: "get navigationStart"
|
||||
},
|
||||
set: {
|
||||
type: "undefined"
|
||||
}
|
||||
},
|
||||
unloadEventStart: {
|
||||
configurable: true,
|
||||
enumerable: true,
|
||||
get: {
|
||||
type: "object",
|
||||
actor: "server1.conn1.child1/obj35",
|
||||
class: "Function",
|
||||
extensible: true,
|
||||
frozen: false,
|
||||
sealed: false,
|
||||
name: "get unloadEventStart",
|
||||
displayName: "get unloadEventStart"
|
||||
},
|
||||
set: {
|
||||
type: "undefined"
|
||||
}
|
||||
},
|
||||
unloadEventEnd: {
|
||||
configurable: true,
|
||||
enumerable: true,
|
||||
get: {
|
||||
type: "object",
|
||||
actor: "server1.conn1.child1/obj36",
|
||||
class: "Function",
|
||||
extensible: true,
|
||||
frozen: false,
|
||||
sealed: false,
|
||||
name: "get unloadEventEnd",
|
||||
displayName: "get unloadEventEnd"
|
||||
},
|
||||
set: {
|
||||
type: "undefined"
|
||||
}
|
||||
},
|
||||
redirectStart: {
|
||||
configurable: true,
|
||||
enumerable: true,
|
||||
get: {
|
||||
type: "object",
|
||||
actor: "server1.conn1.child1/obj37",
|
||||
class: "Function",
|
||||
extensible: true,
|
||||
frozen: false,
|
||||
sealed: false,
|
||||
name: "get redirectStart",
|
||||
displayName: "get redirectStart"
|
||||
},
|
||||
set: {
|
||||
type: "undefined"
|
||||
}
|
||||
},
|
||||
redirectEnd: {
|
||||
configurable: true,
|
||||
enumerable: true,
|
||||
get: {
|
||||
type: "object",
|
||||
actor: "server1.conn1.child1/obj38",
|
||||
class: "Function",
|
||||
extensible: true,
|
||||
frozen: false,
|
||||
sealed: false,
|
||||
name: "get redirectEnd",
|
||||
displayName: "get redirectEnd"
|
||||
},
|
||||
set: {
|
||||
type: "undefined"
|
||||
}
|
||||
},
|
||||
fetchStart: {
|
||||
configurable: true,
|
||||
enumerable: true,
|
||||
get: {
|
||||
type: "object",
|
||||
actor: "server1.conn1.child1/obj39",
|
||||
class: "Function",
|
||||
extensible: true,
|
||||
frozen: false,
|
||||
sealed: false,
|
||||
name: "get fetchStart",
|
||||
displayName: "get fetchStart"
|
||||
},
|
||||
set: {
|
||||
type: "undefined"
|
||||
}
|
||||
},
|
||||
domainLookupStart: {
|
||||
configurable: true,
|
||||
enumerable: true,
|
||||
get: {
|
||||
type: "object",
|
||||
actor: "server1.conn1.child1/obj40",
|
||||
class: "Function",
|
||||
extensible: true,
|
||||
frozen: false,
|
||||
sealed: false,
|
||||
name: "get domainLookupStart",
|
||||
displayName: "get domainLookupStart"
|
||||
},
|
||||
set: {
|
||||
type: "undefined"
|
||||
}
|
||||
},
|
||||
domainLookupEnd: {
|
||||
configurable: true,
|
||||
enumerable: true,
|
||||
get: {
|
||||
type: "object",
|
||||
actor: "server1.conn1.child1/obj41",
|
||||
class: "Function",
|
||||
extensible: true,
|
||||
frozen: false,
|
||||
sealed: false,
|
||||
name: "get domainLookupEnd",
|
||||
displayName: "get domainLookupEnd"
|
||||
},
|
||||
set: {
|
||||
type: "undefined"
|
||||
}
|
||||
},
|
||||
connectStart: {
|
||||
configurable: true,
|
||||
enumerable: true,
|
||||
get: {
|
||||
type: "object",
|
||||
actor: "server1.conn1.child1/obj42",
|
||||
class: "Function",
|
||||
extensible: true,
|
||||
frozen: false,
|
||||
sealed: false,
|
||||
name: "get connectStart",
|
||||
displayName: "get connectStart"
|
||||
},
|
||||
set: {
|
||||
type: "undefined"
|
||||
}
|
||||
}
|
||||
},
|
||||
ownPropertiesLength: 23
|
||||
}
|
||||
},
|
||||
ownProperties: {},
|
||||
safeGetterValues: {
|
||||
navigationStart: {
|
||||
getterValue: 1500967716401,
|
||||
getterPrototypeLevel: 1,
|
||||
enumerable: true,
|
||||
writable: true
|
||||
},
|
||||
unloadEventStart: {
|
||||
getterValue: 0,
|
||||
getterPrototypeLevel: 1,
|
||||
enumerable: true,
|
||||
writable: true
|
||||
},
|
||||
unloadEventEnd: {
|
||||
getterValue: 0,
|
||||
getterPrototypeLevel: 1,
|
||||
enumerable: true,
|
||||
writable: true
|
||||
},
|
||||
redirectStart: {
|
||||
getterValue: 0,
|
||||
getterPrototypeLevel: 1,
|
||||
enumerable: true,
|
||||
writable: true
|
||||
},
|
||||
redirectEnd: {
|
||||
getterValue: 0,
|
||||
getterPrototypeLevel: 1,
|
||||
enumerable: true,
|
||||
writable: true
|
||||
},
|
||||
fetchStart: {
|
||||
getterValue: 1500967716401,
|
||||
getterPrototypeLevel: 1,
|
||||
enumerable: true,
|
||||
writable: true
|
||||
},
|
||||
domainLookupStart: {
|
||||
getterValue: 1500967716401,
|
||||
getterPrototypeLevel: 1,
|
||||
enumerable: true,
|
||||
writable: true
|
||||
},
|
||||
domainLookupEnd: {
|
||||
getterValue: 1500967716401,
|
||||
getterPrototypeLevel: 1,
|
||||
enumerable: true,
|
||||
writable: true
|
||||
},
|
||||
connectStart: {
|
||||
getterValue: 1500967716401,
|
||||
getterPrototypeLevel: 1,
|
||||
enumerable: true,
|
||||
writable: true
|
||||
},
|
||||
connectEnd: {
|
||||
getterValue: 1500967716401,
|
||||
getterPrototypeLevel: 1,
|
||||
enumerable: true,
|
||||
writable: true
|
||||
},
|
||||
secureConnectionStart: {
|
||||
getterValue: 1500967716401,
|
||||
getterPrototypeLevel: 1,
|
||||
enumerable: true,
|
||||
writable: true
|
||||
},
|
||||
requestStart: {
|
||||
getterValue: 1500967716401,
|
||||
getterPrototypeLevel: 1,
|
||||
enumerable: true,
|
||||
writable: true
|
||||
},
|
||||
responseStart: {
|
||||
getterValue: 1500967716401,
|
||||
getterPrototypeLevel: 1,
|
||||
enumerable: true,
|
||||
writable: true
|
||||
},
|
||||
responseEnd: {
|
||||
getterValue: 1500967716401,
|
||||
getterPrototypeLevel: 1,
|
||||
enumerable: true,
|
||||
writable: true
|
||||
},
|
||||
domLoading: {
|
||||
getterValue: 1500967716426,
|
||||
getterPrototypeLevel: 1,
|
||||
enumerable: true,
|
||||
writable: true
|
||||
},
|
||||
domInteractive: {
|
||||
getterValue: 1500967716552,
|
||||
getterPrototypeLevel: 1,
|
||||
enumerable: true,
|
||||
writable: true
|
||||
},
|
||||
domContentLoadedEventStart: {
|
||||
getterValue: 1500967716696,
|
||||
getterPrototypeLevel: 1,
|
||||
enumerable: true,
|
||||
writable: true
|
||||
},
|
||||
domContentLoadedEventEnd: {
|
||||
getterValue: 1500967716715,
|
||||
getterPrototypeLevel: 1,
|
||||
enumerable: true,
|
||||
writable: true
|
||||
},
|
||||
domComplete: {
|
||||
getterValue: 1500967716719,
|
||||
getterPrototypeLevel: 1,
|
||||
enumerable: true,
|
||||
writable: true
|
||||
},
|
||||
loadEventStart: {
|
||||
getterValue: 1500967716719,
|
||||
getterPrototypeLevel: 1,
|
||||
enumerable: true,
|
||||
writable: true
|
||||
},
|
||||
loadEventEnd: {
|
||||
getterValue: 1500967716720,
|
||||
getterPrototypeLevel: 1,
|
||||
enumerable: true,
|
||||
writable: true
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = stubs;
|
|
@ -0,0 +1,17 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
|
||||
|
||||
function LongStringClient(grip, overrides) {
|
||||
return {
|
||||
grip,
|
||||
substring: function() {
|
||||
return Promise.resolve({
|
||||
fullText: ""
|
||||
});
|
||||
},
|
||||
...overrides
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = LongStringClient;
|
|
@ -0,0 +1,46 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
|
||||
|
||||
function ObjectClient(grip, overrides) {
|
||||
return {
|
||||
grip,
|
||||
enumEntries: function() {
|
||||
return Promise.resolve({
|
||||
iterator: this.getIterator({
|
||||
ownProperties: {}
|
||||
})
|
||||
});
|
||||
},
|
||||
enumProperties: function(options) {
|
||||
return Promise.resolve({
|
||||
iterator: this.getIterator({
|
||||
ownProperties: {}
|
||||
})
|
||||
});
|
||||
},
|
||||
enumSymbols: function() {
|
||||
return Promise.resolve({
|
||||
iterator: this.getIterator({
|
||||
ownSymbols: []
|
||||
})
|
||||
});
|
||||
},
|
||||
getPrototype: function() {
|
||||
return Promise.resolve({
|
||||
prototype: {}
|
||||
});
|
||||
},
|
||||
// Declared here so we can override it.
|
||||
getIterator(res) {
|
||||
return {
|
||||
slice: function(start, count) {
|
||||
return Promise.resolve(res);
|
||||
}
|
||||
};
|
||||
},
|
||||
...overrides
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = ObjectClient;
|
|
@ -0,0 +1,63 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`ObjectInspector - renders renders as expected 1`] = `
|
||||
"
|
||||
▶︎ {…}
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`ObjectInspector - renders renders as expected 2`] = `
|
||||
"
|
||||
▶︎ Object { p0: \\"0\\", p1: \\"1\\", p2: \\"2\\", … }
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`ObjectInspector - renders renders as expected 3`] = `
|
||||
"
|
||||
▶︎ Object { p0: \\"0\\", p1: \\"1\\", p2: \\"2\\", p3: \\"3\\", p4: \\"4\\", p5: \\"5\\", p6: \\"6\\", p7: \\"7\\", p8: \\"8\\", p9: \\"9\\", … }
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`ObjectInspector - renders renders as expected 4`] = `
|
||||
"
|
||||
▶︎ {…}
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`ObjectInspector - renders renders as expected when not provided a name 1`] = `
|
||||
"
|
||||
▶︎ Object { p0: \\"0\\", p1: \\"1\\", p2: \\"2\\", … }
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`ObjectInspector - renders renders block nodes as expected 1`] = `
|
||||
"
|
||||
▼ ☲ Block
|
||||
| a: 30
|
||||
| b: 32
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`ObjectInspector - renders renders objects as expected when provided a name 1`] = `
|
||||
"
|
||||
▶︎ myproperty: Object { p0: \\"0\\", p1: \\"1\\", p2: \\"2\\", … }
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`ObjectInspector - renders renders primitives as expected when provided a name 1`] = `
|
||||
"
|
||||
myproperty: 42
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`ObjectInspector - renders updates when the root changes 1`] = `
|
||||
"
|
||||
[ ▶︎ Object { p0: \\"0\\", p1: \\"1\\", p2: \\"2\\", p3: \\"3\\", p4: \\"4\\", p5: \\"5\\", p6: \\"6\\", p7: \\"7\\", p8: \\"8\\", p9: \\"9\\", … } ]
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`ObjectInspector - renders updates when the root changes 2`] = `
|
||||
"
|
||||
[ ▶︎ Object { a: \\"a\\", b: \\"b\\", c: \\"c\\" } ]
|
||||
"
|
||||
`;
|
|
@ -0,0 +1,148 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`ObjectInspector - classnames has the expected class 1`] = `
|
||||
<div
|
||||
className="tree object-inspector"
|
||||
onBlur={[Function]}
|
||||
onFocus={[Function]}
|
||||
onKeyDown={[Function]}
|
||||
onKeyPress={[Function]}
|
||||
onKeyUp={[Function]}
|
||||
role="tree"
|
||||
style={
|
||||
Object {
|
||||
"margin": 0,
|
||||
"padding": 0,
|
||||
}
|
||||
}
|
||||
tabIndex="0"
|
||||
>
|
||||
<div
|
||||
aria-level={1}
|
||||
className="tree-node"
|
||||
data-expandable={false}
|
||||
id="root"
|
||||
onClick={[Function]}
|
||||
role="treeitem"
|
||||
>
|
||||
<div
|
||||
className="node object-node"
|
||||
onClick={[Function]}
|
||||
>
|
||||
<span
|
||||
className="object-label"
|
||||
>
|
||||
root
|
||||
</span>
|
||||
<span
|
||||
className="object-delimiter"
|
||||
>
|
||||
:
|
||||
</span>
|
||||
<span
|
||||
className="objectBox objectBox-number"
|
||||
>
|
||||
42
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`ObjectInspector - classnames has the inline class when inline prop is true 1`] = `
|
||||
<div
|
||||
className="tree inline object-inspector"
|
||||
onBlur={[Function]}
|
||||
onFocus={[Function]}
|
||||
onKeyDown={[Function]}
|
||||
onKeyPress={[Function]}
|
||||
onKeyUp={[Function]}
|
||||
role="tree"
|
||||
style={
|
||||
Object {
|
||||
"margin": 0,
|
||||
"padding": 0,
|
||||
}
|
||||
}
|
||||
tabIndex="0"
|
||||
>
|
||||
<div
|
||||
aria-level={1}
|
||||
className="tree-node"
|
||||
data-expandable={false}
|
||||
id="root"
|
||||
onClick={[Function]}
|
||||
role="treeitem"
|
||||
>
|
||||
<div
|
||||
className="node object-node"
|
||||
onClick={[Function]}
|
||||
>
|
||||
<span
|
||||
className="object-label"
|
||||
>
|
||||
root
|
||||
</span>
|
||||
<span
|
||||
className="object-delimiter"
|
||||
>
|
||||
:
|
||||
</span>
|
||||
<span
|
||||
className="objectBox objectBox-number"
|
||||
>
|
||||
42
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`ObjectInspector - classnames has the nowrap class when disableWrap prop is true 1`] = `
|
||||
<div
|
||||
className="tree nowrap object-inspector"
|
||||
onBlur={[Function]}
|
||||
onFocus={[Function]}
|
||||
onKeyDown={[Function]}
|
||||
onKeyPress={[Function]}
|
||||
onKeyUp={[Function]}
|
||||
role="tree"
|
||||
style={
|
||||
Object {
|
||||
"margin": 0,
|
||||
"padding": 0,
|
||||
}
|
||||
}
|
||||
tabIndex="0"
|
||||
>
|
||||
<div
|
||||
aria-level={1}
|
||||
className="tree-node"
|
||||
data-expandable={false}
|
||||
id="root"
|
||||
onClick={[Function]}
|
||||
role="treeitem"
|
||||
>
|
||||
<div
|
||||
className="node object-node"
|
||||
onClick={[Function]}
|
||||
>
|
||||
<span
|
||||
className="object-label"
|
||||
>
|
||||
root
|
||||
</span>
|
||||
<span
|
||||
className="object-delimiter"
|
||||
>
|
||||
:
|
||||
</span>
|
||||
<span
|
||||
className="objectBox objectBox-number"
|
||||
>
|
||||
42
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
|
@ -0,0 +1,67 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`ObjectInspector - entries calls ObjectClient.enumEntries when expected 1`] = `
|
||||
"
|
||||
▼ Map(11)
|
||||
| ▶︎ <entries>
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`ObjectInspector - entries calls ObjectClient.enumEntries when expected 2`] = `
|
||||
"
|
||||
▼ Map(11)
|
||||
[ | ▼ <entries> ]
|
||||
| | ▶︎ 0: \\"key-0\\" → \\"value-0\\"
|
||||
| | ▶︎ 1: \\"key-1\\" → \\"value-1\\"
|
||||
| | ▶︎ 2: \\"key-2\\" → \\"value-2\\"
|
||||
| | ▶︎ 3: \\"key-3\\" → \\"value-3\\"
|
||||
| | ▶︎ 4: \\"key-4\\" → \\"value-4\\"
|
||||
| | ▶︎ 5: \\"key-5\\" → \\"value-5\\"
|
||||
| | ▶︎ 6: \\"key-6\\" → \\"value-6\\"
|
||||
| | ▶︎ 7: \\"key-7\\" → \\"value-7\\"
|
||||
| | ▶︎ 8: \\"key-8\\" → \\"value-8\\"
|
||||
| | ▶︎ 9: \\"key-9\\" → \\"value-9\\"
|
||||
| | ▶︎ 10: \\"key-10\\" → \\"value-10\\"
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`ObjectInspector - entries calls ObjectClient.enumEntries when expected 3`] = `
|
||||
"
|
||||
▼ Map(11)
|
||||
[ | ▶︎ <entries> ]
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`ObjectInspector - entries calls ObjectClient.enumEntries when expected 4`] = `
|
||||
"
|
||||
▼ Map(11)
|
||||
[ | ▼ <entries> ]
|
||||
| | ▶︎ 0: \\"key-0\\" → \\"value-0\\"
|
||||
| | ▶︎ 1: \\"key-1\\" → \\"value-1\\"
|
||||
| | ▶︎ 2: \\"key-2\\" → \\"value-2\\"
|
||||
| | ▶︎ 3: \\"key-3\\" → \\"value-3\\"
|
||||
| | ▶︎ 4: \\"key-4\\" → \\"value-4\\"
|
||||
| | ▶︎ 5: \\"key-5\\" → \\"value-5\\"
|
||||
| | ▶︎ 6: \\"key-6\\" → \\"value-6\\"
|
||||
| | ▶︎ 7: \\"key-7\\" → \\"value-7\\"
|
||||
| | ▶︎ 8: \\"key-8\\" → \\"value-8\\"
|
||||
| | ▶︎ 9: \\"key-9\\" → \\"value-9\\"
|
||||
| | ▶︎ 10: \\"key-10\\" → \\"value-10\\"
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`ObjectInspector - entries renders Object with entries as expected 1`] = `
|
||||
"
|
||||
▼ Map(2)
|
||||
| size: 2
|
||||
| ▼ <entries>
|
||||
| | ▼ 0: Symbol(a) → \\"value-a\\"
|
||||
| | | <key>: Symbol(a)
|
||||
| | | <value>: \\"value-a\\"
|
||||
| | ▼ 1: Symbol(b) → \\"value-b\\"
|
||||
| | | <key>: Symbol(b)
|
||||
| | | <value>: \\"value-b\\"
|
||||
| ▼ <prototype>: {…}
|
||||
| | <prototype>: Object { }
|
||||
"
|
||||
`;
|
|
@ -0,0 +1,151 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`ObjectInspector - state does not expand if the user selected some text 1`] = `
|
||||
"
|
||||
▶︎ Object { p0: \\"0\\", p1: \\"1\\", p2: \\"2\\", p3: \\"3\\", p4: \\"4\\", p5: \\"5\\", p6: \\"6\\", p7: \\"7\\", p8: \\"8\\", p9: \\"9\\", … }
|
||||
▶︎ Proxy { <target>: {…}, <handler>: (3) […] }
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`ObjectInspector - state does not expand if the user selected some text 2`] = `
|
||||
"
|
||||
▶︎ Object { p0: \\"0\\", p1: \\"1\\", p2: \\"2\\", p3: \\"3\\", p4: \\"4\\", p5: \\"5\\", p6: \\"6\\", p7: \\"7\\", p8: \\"8\\", p9: \\"9\\", … }
|
||||
▶︎ Proxy { <target>: {…}, <handler>: (3) […] }
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`ObjectInspector - state does not throw when expanding a block node 1`] = `
|
||||
"
|
||||
▶︎ ☲ Block
|
||||
▶︎ Proxy: Proxy { <target>: {…}, <handler>: (3) […] }
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`ObjectInspector - state does not throw when expanding a block node 2`] = `
|
||||
"
|
||||
[ ▼ ☲ Block ]
|
||||
| a: 30
|
||||
| b: 32
|
||||
▶︎ Proxy: Proxy { <target>: {…}, <handler>: (3) […] }
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`ObjectInspector - state expanding a getter returning a longString does not throw 1`] = `
|
||||
"
|
||||
▼ {…}
|
||||
| ▼ baseVal: \\"data:image/png;base64,initial<<<<\\"
|
||||
▶︎ Proxy { <target>: {…}, <handler>: (3) […] }
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`ObjectInspector - state expands if user selected some text and clicked the arrow 1`] = `
|
||||
"
|
||||
▶︎ Object { p0: \\"0\\", p1: \\"1\\", p2: \\"2\\", p3: \\"3\\", p4: \\"4\\", p5: \\"5\\", p6: \\"6\\", p7: \\"7\\", p8: \\"8\\", p9: \\"9\\", … }
|
||||
▶︎ Proxy { <target>: {…}, <handler>: (3) […] }
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`ObjectInspector - state expands if user selected some text and clicked the arrow 2`] = `
|
||||
"
|
||||
[ ▼ {…} ]
|
||||
| a: 1
|
||||
| Symbol(): \\"hello\\"
|
||||
| ▶︎ <prototype>: Object { … }
|
||||
▶︎ Proxy { <target>: {…}, <handler>: (3) […] }
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`ObjectInspector - state has the expected expandedPaths state when clicking nodes 1`] = `
|
||||
"
|
||||
▶︎ Object { p0: \\"0\\", p1: \\"1\\", p2: \\"2\\", p3: \\"3\\", p4: \\"4\\", p5: \\"5\\", p6: \\"6\\", p7: \\"7\\", p8: \\"8\\", p9: \\"9\\", … }
|
||||
▶︎ Proxy { <target>: {…}, <handler>: (3) […] }
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`ObjectInspector - state has the expected expandedPaths state when clicking nodes 2`] = `
|
||||
"
|
||||
[ ▼ {…} ]
|
||||
| a: 1
|
||||
| Symbol(): \\"hello\\"
|
||||
| ▶︎ <prototype>: Object { … }
|
||||
▶︎ Proxy { <target>: {…}, <handler>: (3) […] }
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`ObjectInspector - state has the expected expandedPaths state when clicking nodes 3`] = `
|
||||
"
|
||||
[ ▶︎ Object { p0: \\"0\\", p1: \\"1\\", p2: \\"2\\", p3: \\"3\\", p4: \\"4\\", p5: \\"5\\", p6: \\"6\\", p7: \\"7\\", p8: \\"8\\", p9: \\"9\\", … } ]
|
||||
▶︎ Proxy { <target>: {…}, <handler>: (3) […] }
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`ObjectInspector - state has the expected expandedPaths state when clicking nodes 4`] = `
|
||||
"
|
||||
▶︎ Object { p0: \\"0\\", p1: \\"1\\", p2: \\"2\\", p3: \\"3\\", p4: \\"4\\", p5: \\"5\\", p6: \\"6\\", p7: \\"7\\", p8: \\"8\\", p9: \\"9\\", … }
|
||||
[ ▼ Proxy ]
|
||||
| ▶︎ <target>: Object { … }
|
||||
| ▶︎ <handler>: Array(3) [ … ]
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`ObjectInspector - state has the expected expandedPaths state when clicking nodes 5`] = `
|
||||
"
|
||||
[ ▼ {…} ]
|
||||
| a: 1
|
||||
| Symbol(): \\"hello\\"
|
||||
| ▶︎ <prototype>: Object { … }
|
||||
▼ Proxy
|
||||
| ▶︎ <target>: Object { … }
|
||||
| ▶︎ <handler>: Array(3) [ … ]
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`ObjectInspector - state has the expected state when expanding a node 1`] = `
|
||||
"
|
||||
▶︎ Object { p0: \\"0\\", p1: \\"1\\", p2: \\"2\\", p3: \\"3\\", p4: \\"4\\", p5: \\"5\\", p6: \\"6\\", p7: \\"7\\", p8: \\"8\\", p9: \\"9\\", … }
|
||||
▶︎ Proxy { <target>: {…}, <handler>: (3) […] }
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`ObjectInspector - state has the expected state when expanding a node 2`] = `
|
||||
"
|
||||
[ ▼ {…} ]
|
||||
| ▶︎ <prototype>: Object { }
|
||||
▶︎ Proxy { <target>: {…}, <handler>: (3) […] }
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`ObjectInspector - state has the expected state when expanding a node 3`] = `
|
||||
"
|
||||
▼ {…}
|
||||
[ | ▼ <prototype>: {} ]
|
||||
| | ▶︎ <prototype>: Object { }
|
||||
▶︎ Proxy { <target>: {…}, <handler>: (3) […] }
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`ObjectInspector - state has the expected state when expanding a proxy node 1`] = `
|
||||
"
|
||||
▶︎ Object { p0: \\"0\\", p1: \\"1\\", p2: \\"2\\", p3: \\"3\\", p4: \\"4\\", p5: \\"5\\", p6: \\"6\\", p7: \\"7\\", p8: \\"8\\", p9: \\"9\\", … }
|
||||
▶︎ Proxy { <target>: {…}, <handler>: (3) […] }
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`ObjectInspector - state has the expected state when expanding a proxy node 2`] = `
|
||||
"
|
||||
▶︎ Object { p0: \\"0\\", p1: \\"1\\", p2: \\"2\\", p3: \\"3\\", p4: \\"4\\", p5: \\"5\\", p6: \\"6\\", p7: \\"7\\", p8: \\"8\\", p9: \\"9\\", … }
|
||||
[ ▼ Proxy ]
|
||||
| ▶︎ <target>: Object { … }
|
||||
| ▶︎ <handler>: Array(3) [ … ]
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`ObjectInspector - state has the expected state when expanding a proxy node 3`] = `
|
||||
"
|
||||
▶︎ Object { p0: \\"0\\", p1: \\"1\\", p2: \\"2\\", p3: \\"3\\", p4: \\"4\\", p5: \\"5\\", p6: \\"6\\", p7: \\"7\\", p8: \\"8\\", p9: \\"9\\", … }
|
||||
▼ Proxy
|
||||
| ▶︎ <target>: Object { … }
|
||||
[ | ▼ <handler>: (3) […] ]
|
||||
| | <prototype>: Object { }
|
||||
"
|
||||
`;
|
|
@ -0,0 +1,51 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`ObjectInspector - getters & setters onInvokeGetterButtonClick + getter & setter 1`] = `
|
||||
"
|
||||
▼ root
|
||||
| x: (>>)
|
||||
| ▶︎ <get x()>: function x()
|
||||
| ▶︎ <set x()>: function x()
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`ObjectInspector - getters & setters onInvokeGetterButtonClick + getter 1`] = `
|
||||
"
|
||||
▼ root
|
||||
| x: (>>)
|
||||
| ▶︎ <get x()>: function x()
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`ObjectInspector - getters & setters onInvokeGetterButtonClick + setter 1`] = `
|
||||
"
|
||||
▼ root
|
||||
| x: Setter
|
||||
| ▶︎ <set x()>: function x()
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`ObjectInspector - getters & setters renders getters and setters as expected 1`] = `
|
||||
"
|
||||
▼ root
|
||||
| x: Getter & Setter
|
||||
| ▶︎ <get x()>: function x()
|
||||
| ▶︎ <set x()>: function x()
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`ObjectInspector - getters & setters renders getters as expected 1`] = `
|
||||
"
|
||||
▼ root
|
||||
| x: Getter
|
||||
| ▶︎ <get x()>: function x()
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`ObjectInspector - getters & setters renders setters as expected 1`] = `
|
||||
"
|
||||
▼ root
|
||||
| x: Setter
|
||||
| ▶︎ <set x()>: function x()
|
||||
"
|
||||
`;
|
|
@ -0,0 +1,55 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`ObjectInspector - keyboard navigation works as expected 1`] = `
|
||||
"
|
||||
▶︎ Object { a: \\"a\\", b: \\"b\\", c: \\"c\\" }
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`ObjectInspector - keyboard navigation works as expected 2`] = `
|
||||
"
|
||||
[ ▶︎ Object { a: \\"a\\", b: \\"b\\", c: \\"c\\" } ]
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`ObjectInspector - keyboard navigation works as expected 3`] = `
|
||||
"
|
||||
[ ▼ {…} ]
|
||||
| <prototype>: Object { }
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`ObjectInspector - keyboard navigation works as expected 4`] = `
|
||||
"
|
||||
▼ {…}
|
||||
[ | <prototype>: Object { } ]
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`ObjectInspector - keyboard navigation works as expected 5`] = `
|
||||
"
|
||||
[ ▼ {…} ]
|
||||
| <prototype>: Object { }
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`ObjectInspector - keyboard navigation works as expected 6`] = `
|
||||
"
|
||||
▼ {…}
|
||||
[ | <prototype>: Object { } ]
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`ObjectInspector - keyboard navigation works as expected 7`] = `
|
||||
"
|
||||
[ ▼ {…} ]
|
||||
| <prototype>: Object { }
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`ObjectInspector - keyboard navigation works as expected 8`] = `
|
||||
"
|
||||
▼ {…}
|
||||
| <prototype>: Object { }
|
||||
"
|
||||
`;
|
|
@ -0,0 +1,19 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`ObjectInspector - properties renders uninitialized bindings 1`] = `
|
||||
"
|
||||
someFoo: (uninitialized)
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`ObjectInspector - properties renders unmapped bindings 1`] = `
|
||||
"
|
||||
someFoo: (unmapped)
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`ObjectInspector - properties renders unscoped bindings 1`] = `
|
||||
"
|
||||
someFoo: (unscoped)
|
||||
"
|
||||
`;
|
|
@ -0,0 +1,9 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`ObjectInspector - Proxy renders Proxy as expected 1`] = `
|
||||
"
|
||||
▼ Proxy
|
||||
| ▶︎ <target>: Object { … }
|
||||
| ▶︎ <handler>: Array(3) [ … ]
|
||||
"
|
||||
`;
|
|
@ -0,0 +1,306 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`ObjectInspector - dimTopLevelWindow renders collapsed top-level window when dimTopLevelWindow =false 1`] = `
|
||||
<div
|
||||
className="tree object-inspector"
|
||||
onBlur={[Function]}
|
||||
onFocus={[Function]}
|
||||
onKeyDown={[Function]}
|
||||
onKeyPress={[Function]}
|
||||
onKeyUp={[Function]}
|
||||
role="tree"
|
||||
style={
|
||||
Object {
|
||||
"margin": 0,
|
||||
"padding": 0,
|
||||
}
|
||||
}
|
||||
tabIndex="0"
|
||||
>
|
||||
<div
|
||||
aria-expanded={false}
|
||||
aria-level={1}
|
||||
className="tree-node"
|
||||
data-expandable={true}
|
||||
id="Symbol(window)"
|
||||
onClick={[Function]}
|
||||
role="treeitem"
|
||||
>
|
||||
<div
|
||||
className="node object-node"
|
||||
onClick={[Function]}
|
||||
>
|
||||
<button
|
||||
className="arrow"
|
||||
/>
|
||||
<span
|
||||
className="object-label"
|
||||
>
|
||||
window
|
||||
</span>
|
||||
<span
|
||||
className="object-delimiter"
|
||||
>
|
||||
:
|
||||
</span>
|
||||
<span
|
||||
className="objectBox objectBox-Window"
|
||||
data-link-actor-id="server1.conn3.obj198"
|
||||
>
|
||||
<span
|
||||
className="objectTitle"
|
||||
>
|
||||
Window
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`ObjectInspector - dimTopLevelWindow renders sub-level window 1`] = `
|
||||
<div
|
||||
aria-activedescendant="Symbol(root)"
|
||||
className="tree object-inspector"
|
||||
onBlur={[Function]}
|
||||
onFocus={[Function]}
|
||||
onKeyDown={[Function]}
|
||||
onKeyPress={[Function]}
|
||||
onKeyUp={[Function]}
|
||||
role="tree"
|
||||
style={
|
||||
Object {
|
||||
"margin": 0,
|
||||
"padding": 0,
|
||||
}
|
||||
}
|
||||
tabIndex="0"
|
||||
>
|
||||
<div
|
||||
aria-expanded={true}
|
||||
aria-level={1}
|
||||
className="tree-node focused"
|
||||
data-expandable={true}
|
||||
id="Symbol(root)"
|
||||
onClick={[Function]}
|
||||
role="treeitem"
|
||||
>
|
||||
<div
|
||||
className="node object-node focused"
|
||||
onClick={[Function]}
|
||||
>
|
||||
<button
|
||||
className="arrow expanded"
|
||||
/>
|
||||
<span
|
||||
className="object-label"
|
||||
>
|
||||
root
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
aria-expanded={false}
|
||||
aria-level={2}
|
||||
className="tree-node"
|
||||
data-expandable={true}
|
||||
id="Symbol(window)"
|
||||
onClick={[Function]}
|
||||
role="treeitem"
|
||||
>
|
||||
<span
|
||||
className="tree-indent"
|
||||
>
|
||||
|
||||
</span>
|
||||
<div
|
||||
className="node object-node"
|
||||
onClick={[Function]}
|
||||
>
|
||||
<button
|
||||
className="arrow"
|
||||
/>
|
||||
<span
|
||||
className="object-label"
|
||||
>
|
||||
window
|
||||
</span>
|
||||
<span
|
||||
className="object-delimiter"
|
||||
>
|
||||
:
|
||||
</span>
|
||||
<span
|
||||
className="objectBox objectBox-Window"
|
||||
data-link-actor-id="server1.conn3.obj198"
|
||||
>
|
||||
<span
|
||||
className="objectTitle"
|
||||
>
|
||||
Window
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`ObjectInspector - dimTopLevelWindow renders window as expected when dimTopLevelWindow is true 1`] = `
|
||||
<div
|
||||
className="tree object-inspector"
|
||||
onBlur={[Function]}
|
||||
onFocus={[Function]}
|
||||
onKeyDown={[Function]}
|
||||
onKeyPress={[Function]}
|
||||
onKeyUp={[Function]}
|
||||
role="tree"
|
||||
style={
|
||||
Object {
|
||||
"margin": 0,
|
||||
"padding": 0,
|
||||
}
|
||||
}
|
||||
tabIndex="0"
|
||||
>
|
||||
<div
|
||||
aria-expanded={false}
|
||||
aria-level={1}
|
||||
className="tree-node"
|
||||
data-expandable={true}
|
||||
id="Symbol(window)"
|
||||
onClick={[Function]}
|
||||
role="treeitem"
|
||||
>
|
||||
<div
|
||||
className="node object-node lessen"
|
||||
onClick={[Function]}
|
||||
>
|
||||
<button
|
||||
className="arrow"
|
||||
/>
|
||||
<span
|
||||
className="object-label"
|
||||
>
|
||||
window
|
||||
</span>
|
||||
<span
|
||||
className="object-delimiter"
|
||||
>
|
||||
:
|
||||
</span>
|
||||
<span
|
||||
className="objectBox objectBox-Window"
|
||||
data-link-actor-id="server1.conn3.obj198"
|
||||
>
|
||||
<span
|
||||
className="objectTitle"
|
||||
>
|
||||
Window
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`ObjectInspector - dimTopLevelWindow renders window as expected when dimTopLevelWindow is true 2`] = `
|
||||
<div
|
||||
aria-activedescendant="Symbol(window)"
|
||||
className="tree object-inspector"
|
||||
onBlur={[Function]}
|
||||
onFocus={[Function]}
|
||||
onKeyDown={[Function]}
|
||||
onKeyPress={[Function]}
|
||||
onKeyUp={[Function]}
|
||||
role="tree"
|
||||
style={
|
||||
Object {
|
||||
"margin": 0,
|
||||
"padding": 0,
|
||||
}
|
||||
}
|
||||
tabIndex="0"
|
||||
>
|
||||
<div
|
||||
aria-expanded={true}
|
||||
aria-level={1}
|
||||
className="tree-node focused"
|
||||
data-expandable={true}
|
||||
id="Symbol(window)"
|
||||
onClick={[Function]}
|
||||
role="treeitem"
|
||||
>
|
||||
<div
|
||||
className="node object-node focused"
|
||||
onClick={[Function]}
|
||||
>
|
||||
<button
|
||||
className="arrow expanded"
|
||||
/>
|
||||
<span
|
||||
className="object-label"
|
||||
>
|
||||
window
|
||||
</span>
|
||||
<span
|
||||
className="object-delimiter"
|
||||
>
|
||||
:
|
||||
</span>
|
||||
<span
|
||||
className="objectBox objectBox-Window"
|
||||
data-link-actor-id="server1.conn3.obj198"
|
||||
>
|
||||
<span
|
||||
className="objectTitle"
|
||||
>
|
||||
Window
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
aria-level={2}
|
||||
className="tree-node"
|
||||
data-expandable={false}
|
||||
id="Symbol(window/<prototype>)"
|
||||
onClick={[Function]}
|
||||
role="treeitem"
|
||||
>
|
||||
<span
|
||||
className="tree-indent"
|
||||
>
|
||||
|
||||
</span>
|
||||
<div
|
||||
className="node object-node lessen"
|
||||
onClick={[Function]}
|
||||
>
|
||||
<span
|
||||
className="object-label"
|
||||
>
|
||||
<prototype>
|
||||
</span>
|
||||
<span
|
||||
className="object-delimiter"
|
||||
>
|
||||
:
|
||||
</span>
|
||||
<span
|
||||
className="objectBox objectBox-object"
|
||||
>
|
||||
<span
|
||||
className="objectLeftBrace"
|
||||
>
|
||||
{
|
||||
</span>
|
||||
<span
|
||||
className="objectRightBrace"
|
||||
>
|
||||
}
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
|
@ -0,0 +1,432 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
|
||||
|
||||
const { mountObjectInspector } = require("../test-utils");
|
||||
const { mount } = require("enzyme");
|
||||
const { createNode, NODE_TYPES } = require("../../utils/node");
|
||||
const repsPath = "../../../reps";
|
||||
const { MODE } = require(`${repsPath}/constants`);
|
||||
const { Rep } = require(`${repsPath}/rep`);
|
||||
const {
|
||||
formatObjectInspector,
|
||||
waitForDispatch,
|
||||
waitForLoadedProperties
|
||||
} = require("../test-utils");
|
||||
const ObjectClient = require("../__mocks__/object-client");
|
||||
const gripRepStubs = require(`${repsPath}/stubs/grip`);
|
||||
|
||||
function generateDefaults(overrides) {
|
||||
return {
|
||||
autoExpandDepth: 0,
|
||||
...overrides
|
||||
};
|
||||
}
|
||||
|
||||
function mountOI(props, { initialState } = {}) {
|
||||
const client = {
|
||||
createObjectClient: grip => ObjectClient(grip)
|
||||
};
|
||||
|
||||
const obj = mountObjectInspector({
|
||||
client,
|
||||
props: generateDefaults(props),
|
||||
initialState: {
|
||||
objectInspector: {
|
||||
...initialState,
|
||||
evaluations: new Map()
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
function renderOI(props, opts) {
|
||||
return mountOI(props, opts).wrapper;
|
||||
}
|
||||
|
||||
describe("ObjectInspector - renders", () => {
|
||||
it("renders as expected", () => {
|
||||
const stub = gripRepStubs.get("testMoreThanMaxProps");
|
||||
|
||||
const renderObjectInspector = mode =>
|
||||
renderOI({
|
||||
roots: [
|
||||
{
|
||||
path: "root",
|
||||
contents: {
|
||||
value: stub
|
||||
}
|
||||
}
|
||||
],
|
||||
mode
|
||||
});
|
||||
|
||||
const renderRep = mode => Rep({ object: stub, mode });
|
||||
|
||||
const tinyOi = renderObjectInspector(MODE.TINY);
|
||||
expect(tinyOi.find(".arrow").exists()).toBeTruthy();
|
||||
expect(tinyOi.contains(renderRep(MODE.TINY))).toBeTruthy();
|
||||
expect(formatObjectInspector(tinyOi)).toMatchSnapshot();
|
||||
|
||||
const shortOi = renderObjectInspector(MODE.SHORT);
|
||||
expect(shortOi.find(".arrow").exists()).toBeTruthy();
|
||||
expect(shortOi.contains(renderRep(MODE.SHORT))).toBeTruthy();
|
||||
expect(formatObjectInspector(shortOi)).toMatchSnapshot();
|
||||
|
||||
const longOi = renderObjectInspector(MODE.LONG);
|
||||
expect(longOi.find(".arrow").exists()).toBeTruthy();
|
||||
expect(longOi.contains(renderRep(MODE.LONG))).toBeTruthy();
|
||||
expect(formatObjectInspector(longOi)).toMatchSnapshot();
|
||||
|
||||
const oi = renderObjectInspector();
|
||||
expect(oi.find(".arrow").exists()).toBeTruthy();
|
||||
// When no mode is provided, it defaults to TINY mode to render the Rep.
|
||||
expect(oi.contains(renderRep(MODE.TINY))).toBeTruthy();
|
||||
expect(formatObjectInspector(oi)).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("directly renders a Rep when the stub is not expandable", () => {
|
||||
const object = 42;
|
||||
|
||||
const renderObjectInspector = mode =>
|
||||
renderOI({
|
||||
roots: [
|
||||
{
|
||||
path: "root",
|
||||
contents: {
|
||||
value: object
|
||||
}
|
||||
}
|
||||
],
|
||||
mode
|
||||
});
|
||||
|
||||
const renderRep = mode => mount(Rep({ object, mode }));
|
||||
|
||||
const tinyOi = renderObjectInspector(MODE.TINY);
|
||||
expect(tinyOi.find(".arrow").exists()).toBeFalsy();
|
||||
expect(tinyOi.html()).toEqual(renderRep(MODE.TINY).html());
|
||||
|
||||
const shortOi = renderObjectInspector(MODE.SHORT);
|
||||
expect(shortOi.find(".arrow").exists()).toBeFalsy();
|
||||
expect(shortOi.html()).toEqual(renderRep(MODE.SHORT).html());
|
||||
|
||||
const longOi = renderObjectInspector(MODE.LONG);
|
||||
expect(longOi.find(".arrow").exists()).toBeFalsy();
|
||||
expect(longOi.html()).toEqual(renderRep(MODE.LONG).html());
|
||||
|
||||
const oi = renderObjectInspector();
|
||||
expect(oi.find(".arrow").exists()).toBeFalsy();
|
||||
// When no mode is provided, it defaults to TINY mode to render the Rep.
|
||||
expect(oi.html()).toEqual(renderRep(MODE.TINY).html());
|
||||
});
|
||||
|
||||
it("renders objects as expected when provided a name", () => {
|
||||
const object = gripRepStubs.get("testMoreThanMaxProps");
|
||||
const name = "myproperty";
|
||||
|
||||
const oi = renderOI({
|
||||
roots: [
|
||||
{
|
||||
path: "root",
|
||||
name,
|
||||
contents: {
|
||||
value: object
|
||||
}
|
||||
}
|
||||
],
|
||||
mode: MODE.SHORT
|
||||
});
|
||||
|
||||
expect(oi.find(".object-label").text()).toEqual(name);
|
||||
expect(formatObjectInspector(oi)).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("renders primitives as expected when provided a name", () => {
|
||||
const value = 42;
|
||||
const name = "myproperty";
|
||||
|
||||
const oi = renderOI({
|
||||
roots: [
|
||||
{
|
||||
path: "root",
|
||||
name,
|
||||
contents: { value }
|
||||
}
|
||||
],
|
||||
mode: MODE.SHORT
|
||||
});
|
||||
|
||||
expect(oi.find(".object-label").text()).toEqual(name);
|
||||
expect(formatObjectInspector(oi)).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("renders as expected when not provided a name", () => {
|
||||
const object = gripRepStubs.get("testMoreThanMaxProps");
|
||||
|
||||
const oi = renderOI({
|
||||
roots: [
|
||||
{
|
||||
path: "root",
|
||||
contents: {
|
||||
value: object
|
||||
}
|
||||
}
|
||||
],
|
||||
mode: MODE.SHORT
|
||||
});
|
||||
|
||||
expect(oi.find(".object-label").exists()).toBeFalsy();
|
||||
expect(formatObjectInspector(oi)).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("renders leaves with a shorter mode than the root", async () => {
|
||||
const stub = gripRepStubs.get("testMaxProps");
|
||||
|
||||
const renderObjectInspector = mode =>
|
||||
renderOI(
|
||||
{
|
||||
autoExpandDepth: 1,
|
||||
roots: [
|
||||
{
|
||||
path: "root",
|
||||
contents: {
|
||||
value: stub
|
||||
}
|
||||
}
|
||||
],
|
||||
mode
|
||||
},
|
||||
{
|
||||
initialState: {
|
||||
loadedProperties: new Map([
|
||||
[
|
||||
"root",
|
||||
{
|
||||
ownProperties: Object.keys(stub.preview.ownProperties).reduce(
|
||||
(res, key) => ({
|
||||
[key]: {
|
||||
value: stub
|
||||
},
|
||||
...res
|
||||
}),
|
||||
{}
|
||||
)
|
||||
}
|
||||
]
|
||||
])
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
const renderRep = mode => Rep({ object: stub, mode });
|
||||
|
||||
const tinyOi = renderObjectInspector(MODE.TINY);
|
||||
|
||||
expect(
|
||||
tinyOi
|
||||
.find(".node")
|
||||
.at(1)
|
||||
.contains(renderRep(MODE.TINY))
|
||||
).toBeTruthy();
|
||||
|
||||
const shortOi = renderObjectInspector(MODE.SHORT);
|
||||
expect(
|
||||
shortOi
|
||||
.find(".node")
|
||||
.at(1)
|
||||
.contains(renderRep(MODE.TINY))
|
||||
).toBeTruthy();
|
||||
|
||||
const longOi = renderObjectInspector(MODE.LONG);
|
||||
expect(
|
||||
longOi
|
||||
.find(".node")
|
||||
.at(1)
|
||||
.contains(renderRep(MODE.SHORT))
|
||||
).toBeTruthy();
|
||||
|
||||
const oi = renderObjectInspector();
|
||||
// When no mode is provided, it defaults to TINY mode to render the Rep.
|
||||
expect(
|
||||
oi
|
||||
.find(".node")
|
||||
.at(1)
|
||||
.contains(renderRep(MODE.TINY))
|
||||
).toBeTruthy();
|
||||
});
|
||||
|
||||
it("renders less-important nodes as expected", async () => {
|
||||
const defaultPropertiesNode = createNode({
|
||||
name: "<default>",
|
||||
contents: [],
|
||||
type: NODE_TYPES.DEFAULT_PROPERTIES
|
||||
});
|
||||
|
||||
// The <default properties> node should have the "lessen" class only when
|
||||
// collapsed.
|
||||
let { store, wrapper } = mountOI({
|
||||
roots: [defaultPropertiesNode]
|
||||
});
|
||||
|
||||
let defaultPropertiesElementNode = wrapper.find(".node");
|
||||
expect(defaultPropertiesElementNode.hasClass("lessen")).toBe(true);
|
||||
|
||||
let onPropertiesLoaded = waitForDispatch(store, "NODE_PROPERTIES_LOADED");
|
||||
defaultPropertiesElementNode.simulate("click");
|
||||
await onPropertiesLoaded;
|
||||
wrapper.update();
|
||||
defaultPropertiesElementNode = wrapper.find(".node").first();
|
||||
expect(
|
||||
wrapper
|
||||
.find(".node")
|
||||
.first()
|
||||
.hasClass("lessen")
|
||||
).toBe(false);
|
||||
|
||||
const prototypeNode = createNode({
|
||||
name: "<prototype>",
|
||||
contents: [],
|
||||
type: NODE_TYPES.PROTOTYPE
|
||||
});
|
||||
|
||||
// The <prototype> node should have the "lessen" class only when collapsed.
|
||||
({ wrapper, store } = mountOI({
|
||||
roots: [prototypeNode],
|
||||
injectWaitService: true
|
||||
}));
|
||||
|
||||
let protoElementNode = wrapper.find(".node");
|
||||
expect(protoElementNode.hasClass("lessen")).toBe(true);
|
||||
|
||||
onPropertiesLoaded = waitForDispatch(store, "NODE_PROPERTIES_LOADED");
|
||||
protoElementNode.simulate("click");
|
||||
await onPropertiesLoaded;
|
||||
wrapper.update();
|
||||
|
||||
protoElementNode = wrapper.find(".node").first();
|
||||
expect(protoElementNode.hasClass("lessen")).toBe(false);
|
||||
});
|
||||
|
||||
it("renders block nodes as expected", async () => {
|
||||
const blockNode = createNode({
|
||||
name: "Block",
|
||||
contents: [
|
||||
{
|
||||
name: "a",
|
||||
contents: {
|
||||
value: 30
|
||||
}
|
||||
},
|
||||
{
|
||||
name: "b",
|
||||
contents: {
|
||||
value: 32
|
||||
}
|
||||
}
|
||||
],
|
||||
type: NODE_TYPES.BLOCK
|
||||
});
|
||||
|
||||
const { wrapper, store } = mountOI({
|
||||
roots: [blockNode],
|
||||
autoExpandDepth: 1
|
||||
});
|
||||
|
||||
await waitForLoadedProperties(store, ["Symbol(Block)"]);
|
||||
wrapper.update();
|
||||
|
||||
const blockElementNode = wrapper.find(".node").first();
|
||||
expect(blockElementNode.hasClass("block")).toBe(true);
|
||||
expect(formatObjectInspector(wrapper)).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it.skip("updates when the root changes", async () => {
|
||||
let root = {
|
||||
path: "root",
|
||||
contents: {
|
||||
value: gripRepStubs.get("testMoreThanMaxProps")
|
||||
}
|
||||
};
|
||||
const { wrapper } = mountOI({
|
||||
roots: [root],
|
||||
mode: MODE.LONG,
|
||||
focusedItem: root
|
||||
});
|
||||
|
||||
expect(formatObjectInspector(wrapper)).toMatchSnapshot();
|
||||
|
||||
root = {
|
||||
path: "root-2",
|
||||
contents: {
|
||||
value: gripRepStubs.get("testMaxProps")
|
||||
}
|
||||
};
|
||||
|
||||
wrapper.setProps({
|
||||
roots: [root],
|
||||
focusedItem: root
|
||||
});
|
||||
wrapper.update();
|
||||
expect(formatObjectInspector(wrapper)).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it.skip("updates when the root changes but has same path", async () => {
|
||||
const { wrapper, store } = mountOI({
|
||||
injectWaitService: true,
|
||||
roots: [
|
||||
{
|
||||
path: "root",
|
||||
name: "root",
|
||||
contents: [
|
||||
{
|
||||
name: "a",
|
||||
contents: {
|
||||
value: 30
|
||||
}
|
||||
},
|
||||
{
|
||||
name: "b",
|
||||
contents: {
|
||||
value: 32
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
mode: MODE.LONG
|
||||
});
|
||||
|
||||
wrapper
|
||||
.find(".node")
|
||||
.at(0)
|
||||
.simulate("click");
|
||||
|
||||
const oldTree = formatObjectInspector(wrapper);
|
||||
|
||||
const onRootsChanged = waitForDispatch(store, "ROOTS_CHANGED");
|
||||
|
||||
wrapper.setProps({
|
||||
roots: [
|
||||
{
|
||||
path: "root",
|
||||
name: "root",
|
||||
contents: [
|
||||
{
|
||||
name: "c",
|
||||
contents: {
|
||||
value: "i'm the new node"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
await onRootsChanged;
|
||||
wrapper.update();
|
||||
expect(formatObjectInspector(wrapper)).not.toBe(oldTree);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,51 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
|
||||
|
||||
const ObjectClient = require("../__mocks__/object-client");
|
||||
const { mountObjectInspector } = require("../test-utils");
|
||||
|
||||
function generateDefaults(overrides) {
|
||||
return {
|
||||
autoExpandDepth: 0,
|
||||
roots: [
|
||||
{
|
||||
path: "root",
|
||||
name: "root",
|
||||
contents: { value: 42 }
|
||||
}
|
||||
],
|
||||
...overrides
|
||||
};
|
||||
}
|
||||
|
||||
function mount(props) {
|
||||
const client = { createObjectClient: grip => ObjectClient(grip) };
|
||||
|
||||
return mountObjectInspector({
|
||||
client,
|
||||
props: generateDefaults(props)
|
||||
});
|
||||
}
|
||||
|
||||
describe("ObjectInspector - classnames", () => {
|
||||
it("has the expected class", () => {
|
||||
const { tree } = mount();
|
||||
expect(tree.hasClass("tree")).toBeTruthy();
|
||||
expect(tree.hasClass("inline")).toBeFalsy();
|
||||
expect(tree.hasClass("nowrap")).toBeFalsy();
|
||||
expect(tree).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("has the nowrap class when disableWrap prop is true", () => {
|
||||
const { tree } = mount({ disableWrap: true });
|
||||
expect(tree.hasClass("nowrap")).toBeTruthy();
|
||||
expect(tree).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("has the inline class when inline prop is true", () => {
|
||||
const { tree } = mount({ inline: true });
|
||||
expect(tree.hasClass("inline")).toBeTruthy();
|
||||
expect(tree).toMatchSnapshot();
|
||||
});
|
||||
});
|
|
@ -0,0 +1,92 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
|
||||
|
||||
/* global jest */
|
||||
|
||||
const { mountObjectInspector } = require("../test-utils");
|
||||
const ObjectClient = require("../__mocks__/object-client");
|
||||
const LongStringClient = require("../__mocks__/long-string-client");
|
||||
|
||||
const repsPath = "../../../reps";
|
||||
const longStringStubs = require(`${repsPath}/stubs/long-string`);
|
||||
|
||||
function mount(props) {
|
||||
const substring = jest.fn(() => Promise.resolve({ fullText: "" }));
|
||||
|
||||
const client = {
|
||||
createObjectClient: grip => ObjectClient(grip),
|
||||
createLongStringClient: jest.fn(grip =>
|
||||
LongStringClient(grip, { substring })
|
||||
)
|
||||
};
|
||||
|
||||
const obj = mountObjectInspector({
|
||||
client,
|
||||
props
|
||||
});
|
||||
|
||||
return { ...obj, substring };
|
||||
}
|
||||
|
||||
describe("createLongStringClient", () => {
|
||||
it("is called with the expected object for longString node", () => {
|
||||
const stub = longStringStubs.get("testUnloadedFullText");
|
||||
|
||||
const { client } = mount({
|
||||
autoExpandDepth: 1,
|
||||
roots: [
|
||||
{
|
||||
path: "root",
|
||||
contents: {
|
||||
value: stub
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
expect(client.createLongStringClient.mock.calls[0][0]).toBe(stub);
|
||||
});
|
||||
|
||||
describe("substring", () => {
|
||||
it("is called for longStrings with unloaded full text", () => {
|
||||
const stub = longStringStubs.get("testUnloadedFullText");
|
||||
|
||||
const { substring } = mount({
|
||||
autoExpandDepth: 1,
|
||||
roots: [
|
||||
{
|
||||
path: "root",
|
||||
contents: {
|
||||
value: stub
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
// Third argument is the callback which holds the string response.
|
||||
expect(substring.mock.calls[0]).toHaveLength(3);
|
||||
const [start, length] = substring.mock.calls[0];
|
||||
expect(start).toBe(stub.initial.length);
|
||||
expect(length).toBe(stub.length);
|
||||
});
|
||||
|
||||
it("is not called for longString node w/ loaded full text", () => {
|
||||
const stub = longStringStubs.get("testLoadedFullText");
|
||||
|
||||
const { substring } = mount({
|
||||
autoExpandDepth: 1,
|
||||
roots: [
|
||||
{
|
||||
path: "root",
|
||||
contents: {
|
||||
value: stub
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
expect(substring.mock.calls).toHaveLength(0);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -0,0 +1,109 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
|
||||
|
||||
/* global jest */
|
||||
|
||||
const { mountObjectInspector } = require("../test-utils");
|
||||
const ObjectClient = require("../__mocks__/object-client");
|
||||
|
||||
const {
|
||||
createNode,
|
||||
makeNodesForEntries,
|
||||
makeNumericalBuckets
|
||||
} = require("../../utils/node");
|
||||
|
||||
const repsPath = "../../../reps";
|
||||
const gripRepStubs = require(`${repsPath}/stubs/grip`);
|
||||
const gripArrayRepStubs = require(`${repsPath}/stubs/grip-array`);
|
||||
|
||||
function mount(props, overrides = {}) {
|
||||
const client = {
|
||||
createObjectClient:
|
||||
overrides.createObjectClient || jest.fn(grip => ObjectClient(grip))
|
||||
};
|
||||
|
||||
return mountObjectInspector({
|
||||
client,
|
||||
props
|
||||
});
|
||||
}
|
||||
|
||||
describe("createObjectClient", () => {
|
||||
it("is called with the expected object for regular node", () => {
|
||||
const stub = gripRepStubs.get("testMoreThanMaxProps");
|
||||
const { client } = mount({
|
||||
autoExpandDepth: 1,
|
||||
roots: [
|
||||
{
|
||||
path: "root",
|
||||
contents: {
|
||||
value: stub
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
expect(client.createObjectClient.mock.calls[0][0]).toBe(stub);
|
||||
});
|
||||
|
||||
it("is called with the expected object for entries node", () => {
|
||||
const grip = Symbol();
|
||||
const mapStubNode = createNode({ name: "map", contents: { value: grip } });
|
||||
const entriesNode = makeNodesForEntries(mapStubNode);
|
||||
|
||||
const { client } = mount({
|
||||
autoExpandDepth: 1,
|
||||
roots: [entriesNode]
|
||||
});
|
||||
|
||||
expect(client.createObjectClient.mock.calls[0][0]).toBe(grip);
|
||||
});
|
||||
|
||||
it("is called with the expected object for bucket node", () => {
|
||||
const grip = gripArrayRepStubs.get("testMaxProps");
|
||||
const root = createNode({ name: "root", contents: { value: grip } });
|
||||
const [bucket] = makeNumericalBuckets(root);
|
||||
|
||||
const { client } = mount({
|
||||
autoExpandDepth: 1,
|
||||
roots: [bucket]
|
||||
});
|
||||
expect(client.createObjectClient.mock.calls[0][0]).toBe(grip);
|
||||
});
|
||||
|
||||
it("is called with the expected object for sub-bucket node", () => {
|
||||
const grip = gripArrayRepStubs.get("testMaxProps");
|
||||
const root = createNode({ name: "root", contents: { value: grip } });
|
||||
const [bucket] = makeNumericalBuckets(root);
|
||||
const [subBucket] = makeNumericalBuckets(bucket);
|
||||
|
||||
const { client } = mount({
|
||||
autoExpandDepth: 1,
|
||||
roots: [subBucket]
|
||||
});
|
||||
|
||||
expect(client.createObjectClient.mock.calls[0][0]).toBe(grip);
|
||||
});
|
||||
|
||||
it("doesn't fail when ObjectClient doesn't have expected methods", () => {
|
||||
const stub = gripRepStubs.get("testMoreThanMaxProps");
|
||||
const root = createNode({ name: "root", contents: { value: stub } });
|
||||
|
||||
// Override console.error so we don't spam test results.
|
||||
const originalConsoleError = console.error;
|
||||
console.error = () => {};
|
||||
|
||||
const createObjectClient = x => ({});
|
||||
mount(
|
||||
{
|
||||
autoExpandDepth: 1,
|
||||
roots: [root]
|
||||
},
|
||||
{ createObjectClient }
|
||||
);
|
||||
|
||||
// rollback console.error.
|
||||
console.error = originalConsoleError;
|
||||
});
|
||||
});
|
|
@ -0,0 +1,137 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
|
||||
|
||||
/* global jest */
|
||||
|
||||
const { mountObjectInspector } = require("../test-utils");
|
||||
const { MODE } = require("../../../reps/constants");
|
||||
const {
|
||||
formatObjectInspector,
|
||||
waitForDispatch,
|
||||
waitForLoadedProperties
|
||||
} = require("../test-utils");
|
||||
const gripMapRepStubs = require("../../../reps/stubs/grip-map");
|
||||
const mapStubs = require("../../stubs/map");
|
||||
const ObjectClient = require("../__mocks__/object-client");
|
||||
|
||||
function generateDefaults(overrides) {
|
||||
return {
|
||||
autoExpandDepth: 0,
|
||||
createObjectClient: grip => ObjectClient(grip),
|
||||
...overrides
|
||||
};
|
||||
}
|
||||
|
||||
function getEnumEntriesMock() {
|
||||
return jest.fn(() => ({
|
||||
iterator: {
|
||||
slice: () => mapStubs.get("11-entries")
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
function mount(props, { initialState }) {
|
||||
const enumEntries = getEnumEntriesMock();
|
||||
|
||||
const client = {
|
||||
createObjectClient: grip => ObjectClient(grip, { enumEntries })
|
||||
};
|
||||
const obj = mountObjectInspector({
|
||||
client,
|
||||
props: generateDefaults(props),
|
||||
initialState: {
|
||||
objectInspector: {
|
||||
...initialState,
|
||||
evaluations: new Map()
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return { ...obj, enumEntries };
|
||||
}
|
||||
|
||||
describe("ObjectInspector - entries", () => {
|
||||
it("renders Object with entries as expected", async () => {
|
||||
const stub = gripMapRepStubs.get("testSymbolKeyedMap");
|
||||
|
||||
const { store, wrapper, enumEntries } = mount(
|
||||
{
|
||||
autoExpandDepth: 3,
|
||||
roots: [
|
||||
{
|
||||
path: "root",
|
||||
contents: { value: stub }
|
||||
}
|
||||
],
|
||||
mode: MODE.LONG
|
||||
},
|
||||
{
|
||||
initialState: {
|
||||
loadedProperties: new Map([["root", mapStubs.get("properties")]])
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
await waitForLoadedProperties(store, [
|
||||
"Symbol(root/<entries>/0)",
|
||||
"Symbol(root/<entries>/1)"
|
||||
]);
|
||||
|
||||
wrapper.update();
|
||||
expect(formatObjectInspector(wrapper)).toMatchSnapshot();
|
||||
|
||||
// enumEntries shouldn't have been called since everything
|
||||
// is already in the preview property.
|
||||
expect(enumEntries.mock.calls).toHaveLength(0);
|
||||
});
|
||||
|
||||
it("calls ObjectClient.enumEntries when expected", async () => {
|
||||
const stub = gripMapRepStubs.get("testMoreThanMaxEntries");
|
||||
|
||||
const { wrapper, store, enumEntries } = mount(
|
||||
{
|
||||
autoExpandDepth: 1,
|
||||
injectWaitService: true,
|
||||
roots: [
|
||||
{
|
||||
path: "root",
|
||||
contents: {
|
||||
value: stub
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
initialState: {
|
||||
loadedProperties: new Map([
|
||||
["root", { ownProperties: stub.preview.entries }]
|
||||
])
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
expect(formatObjectInspector(wrapper)).toMatchSnapshot();
|
||||
|
||||
const nodes = wrapper.find(".node");
|
||||
const entriesNode = nodes.at(1);
|
||||
expect(entriesNode.text()).toBe("<entries>");
|
||||
|
||||
const onEntrieLoad = waitForDispatch(store, "NODE_PROPERTIES_LOADED");
|
||||
entriesNode.simulate("click");
|
||||
await onEntrieLoad;
|
||||
wrapper.update();
|
||||
|
||||
expect(formatObjectInspector(wrapper)).toMatchSnapshot();
|
||||
expect(enumEntries.mock.calls).toHaveLength(1);
|
||||
|
||||
entriesNode.simulate("click");
|
||||
expect(formatObjectInspector(wrapper)).toMatchSnapshot();
|
||||
|
||||
entriesNode.simulate("click");
|
||||
|
||||
expect(formatObjectInspector(wrapper)).toMatchSnapshot();
|
||||
// it does not call enumEntries if entries were already loaded.
|
||||
expect(enumEntries.mock.calls).toHaveLength(1);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,169 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
|
||||
|
||||
/* global jest */
|
||||
const { mountObjectInspector } = require("../test-utils");
|
||||
|
||||
const gripRepStubs = require("../../../reps/stubs/grip");
|
||||
const ObjectClient = require("../__mocks__/object-client");
|
||||
|
||||
function generateDefaults(overrides) {
|
||||
return {
|
||||
autoExpandDepth: 0,
|
||||
...overrides
|
||||
};
|
||||
}
|
||||
|
||||
function mount(props) {
|
||||
const client = { createObjectClient: grip => ObjectClient(grip) };
|
||||
|
||||
return mountObjectInspector({
|
||||
client,
|
||||
props: generateDefaults(props)
|
||||
});
|
||||
}
|
||||
|
||||
describe("ObjectInspector - properties", () => {
|
||||
it("calls the onFocus prop when provided one and given focus", () => {
|
||||
const stub = gripRepStubs.get("testMaxProps");
|
||||
const onFocus = jest.fn();
|
||||
|
||||
const { wrapper } = mount({
|
||||
roots: [
|
||||
{
|
||||
path: "root",
|
||||
contents: {
|
||||
value: stub
|
||||
}
|
||||
}
|
||||
],
|
||||
onFocus
|
||||
});
|
||||
|
||||
const node = wrapper.find(".node").first();
|
||||
node.simulate("focus");
|
||||
|
||||
expect(onFocus.mock.calls).toHaveLength(1);
|
||||
});
|
||||
|
||||
it("doesn't call the onFocus when given focus but focusable is false", () => {
|
||||
const stub = gripRepStubs.get("testMaxProps");
|
||||
const onFocus = jest.fn();
|
||||
|
||||
const { wrapper } = mount({
|
||||
focusable: false,
|
||||
roots: [
|
||||
{
|
||||
path: "root",
|
||||
contents: {
|
||||
value: stub
|
||||
}
|
||||
}
|
||||
],
|
||||
onFocus
|
||||
});
|
||||
|
||||
const node = wrapper.find(".node").first();
|
||||
node.simulate("focus");
|
||||
|
||||
expect(onFocus.mock.calls).toHaveLength(0);
|
||||
});
|
||||
|
||||
it("calls onDoubleClick prop when provided one and double clicked", () => {
|
||||
const stub = gripRepStubs.get("testMaxProps");
|
||||
const onDoubleClick = jest.fn();
|
||||
|
||||
const { wrapper } = mount({
|
||||
roots: [
|
||||
{
|
||||
path: "root",
|
||||
contents: {
|
||||
value: stub
|
||||
}
|
||||
}
|
||||
],
|
||||
onDoubleClick
|
||||
});
|
||||
|
||||
const node = wrapper.find(".node").first();
|
||||
node.simulate("doubleclick");
|
||||
|
||||
expect(onDoubleClick.mock.calls).toHaveLength(1);
|
||||
});
|
||||
|
||||
it("calls the onCmdCtrlClick prop when provided and cmd/ctrl-clicked", () => {
|
||||
const stub = gripRepStubs.get("testMaxProps");
|
||||
const onCmdCtrlClick = jest.fn();
|
||||
|
||||
const { wrapper } = mount({
|
||||
roots: [
|
||||
{
|
||||
path: "root",
|
||||
contents: {
|
||||
value: stub
|
||||
}
|
||||
}
|
||||
],
|
||||
onCmdCtrlClick
|
||||
});
|
||||
|
||||
const node = wrapper.find(".node").first();
|
||||
node.simulate("click", { ctrlKey: true });
|
||||
|
||||
expect(onCmdCtrlClick.mock.calls).toHaveLength(1);
|
||||
});
|
||||
|
||||
it("calls the onLabel prop when provided one and label clicked", () => {
|
||||
const stub = gripRepStubs.get("testMaxProps");
|
||||
const onLabelClick = jest.fn();
|
||||
|
||||
const { wrapper } = mount({
|
||||
roots: [
|
||||
{
|
||||
path: "root",
|
||||
name: "Label",
|
||||
contents: {
|
||||
value: stub
|
||||
}
|
||||
}
|
||||
],
|
||||
onLabelClick
|
||||
});
|
||||
|
||||
const label = wrapper.find(".object-label").first();
|
||||
label.simulate("click");
|
||||
|
||||
expect(onLabelClick.mock.calls).toHaveLength(1);
|
||||
});
|
||||
|
||||
it("does not call the onLabel prop when the user selected text", () => {
|
||||
const stub = gripRepStubs.get("testMaxProps");
|
||||
const onLabelClick = jest.fn();
|
||||
|
||||
const { wrapper } = mount({
|
||||
roots: [
|
||||
{
|
||||
path: "root",
|
||||
name: "Label",
|
||||
contents: {
|
||||
value: stub
|
||||
}
|
||||
}
|
||||
],
|
||||
onLabelClick
|
||||
});
|
||||
|
||||
const label = wrapper.find(".object-label").first();
|
||||
|
||||
// Set a selection using the mock.
|
||||
getSelection().setMockSelection("test");
|
||||
|
||||
label.simulate("click");
|
||||
|
||||
expect(onLabelClick.mock.calls).toHaveLength(0);
|
||||
|
||||
// Clear the selection for other tests.
|
||||
getSelection().setMockSelection();
|
||||
});
|
||||
});
|
|
@ -0,0 +1,406 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
|
||||
const { mountObjectInspector } = require("../test-utils");
|
||||
|
||||
const repsPath = "../../../reps";
|
||||
const { MODE } = require(`${repsPath}/constants`);
|
||||
const ObjectClient = require("../__mocks__/object-client");
|
||||
const gripRepStubs = require(`${repsPath}/stubs/grip`);
|
||||
const gripPropertiesStubs = require("../../stubs/grip");
|
||||
const {
|
||||
formatObjectInspector,
|
||||
storeHasExactExpandedPaths,
|
||||
storeHasExpandedPath,
|
||||
storeHasLoadedProperty,
|
||||
waitForDispatch
|
||||
} = require("../test-utils");
|
||||
const { createNode, NODE_TYPES } = require("../../utils/node");
|
||||
const { getActors, getExpandedPaths } = require("../../reducer");
|
||||
|
||||
const protoStub = {
|
||||
prototype: {
|
||||
type: "object",
|
||||
actor: "server2.conn0.child1/obj628",
|
||||
class: "Object"
|
||||
}
|
||||
};
|
||||
|
||||
function generateDefaults(overrides) {
|
||||
return {
|
||||
autoExpandDepth: 0,
|
||||
roots: [
|
||||
{
|
||||
path: "root-1",
|
||||
contents: {
|
||||
value: gripRepStubs.get("testMoreThanMaxProps")
|
||||
}
|
||||
},
|
||||
{
|
||||
path: "root-2",
|
||||
contents: {
|
||||
value: gripRepStubs.get("testProxy")
|
||||
}
|
||||
}
|
||||
],
|
||||
createObjectClient: grip => ObjectClient(grip),
|
||||
mode: MODE.LONG,
|
||||
...overrides
|
||||
};
|
||||
}
|
||||
const LongStringClientMock = require("../__mocks__/long-string-client");
|
||||
|
||||
function mount(props, { initialState } = {}) {
|
||||
const client = {
|
||||
createObjectClient: grip =>
|
||||
ObjectClient(grip, {
|
||||
getPrototype: () => Promise.resolve(protoStub)
|
||||
}),
|
||||
|
||||
createLongStringClient: grip =>
|
||||
LongStringClientMock(grip, {
|
||||
substring: function(initiaLength, length, cb) {
|
||||
cb({
|
||||
substring: "<<<<"
|
||||
});
|
||||
}
|
||||
})
|
||||
};
|
||||
|
||||
return mountObjectInspector({
|
||||
client,
|
||||
props: generateDefaults(props),
|
||||
initialState: {
|
||||
objectInspector: {
|
||||
...initialState,
|
||||
evaluations: new Map()
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
describe("ObjectInspector - state", () => {
|
||||
it("has the expected expandedPaths state when clicking nodes", async () => {
|
||||
const { wrapper, store } = mount(
|
||||
{},
|
||||
{
|
||||
initialState: {
|
||||
loadedProperties: new Map([
|
||||
["root-1", gripPropertiesStubs.get("proto-properties-symbols")]
|
||||
])
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
expect(formatObjectInspector(wrapper)).toMatchSnapshot();
|
||||
let nodes = wrapper.find(".node");
|
||||
|
||||
// Clicking on the root node adds it path to "expandedPaths".
|
||||
const root1 = nodes.at(0);
|
||||
const root2 = nodes.at(1);
|
||||
|
||||
root1.simulate("click");
|
||||
|
||||
expect(storeHasExactExpandedPaths(store, ["root-1"])).toBeTruthy();
|
||||
expect(formatObjectInspector(wrapper)).toMatchSnapshot();
|
||||
//
|
||||
// Clicking on the root node removes it path from "expandedPaths".
|
||||
root1.simulate("click");
|
||||
expect(storeHasExactExpandedPaths(store, [])).toBeTruthy();
|
||||
expect(formatObjectInspector(wrapper)).toMatchSnapshot();
|
||||
|
||||
const onPropertiesLoaded = waitForDispatch(store, "NODE_PROPERTIES_LOADED");
|
||||
root2.simulate("click");
|
||||
await onPropertiesLoaded;
|
||||
expect(storeHasExactExpandedPaths(store, ["root-2"])).toBeTruthy();
|
||||
|
||||
wrapper.update();
|
||||
expect(formatObjectInspector(wrapper)).toMatchSnapshot();
|
||||
|
||||
root1.simulate("click");
|
||||
expect(
|
||||
storeHasExactExpandedPaths(store, ["root-1", "root-2"])
|
||||
).toBeTruthy();
|
||||
expect(formatObjectInspector(wrapper)).toMatchSnapshot();
|
||||
|
||||
nodes = wrapper.find(".node");
|
||||
const propNode = nodes.at(1);
|
||||
const symbolNode = nodes.at(2);
|
||||
const protoNode = nodes.at(3);
|
||||
|
||||
propNode.simulate("click");
|
||||
symbolNode.simulate("click");
|
||||
protoNode.simulate("click");
|
||||
|
||||
expect(
|
||||
storeHasExactExpandedPaths(store, [
|
||||
"root-1",
|
||||
"root-2",
|
||||
"Symbol(root-1/<prototype>)"
|
||||
])
|
||||
).toBeTruthy();
|
||||
|
||||
// The property and symbols have primitive values, and can't be expanded.
|
||||
expect(getExpandedPaths(store.getState()).size).toBe(3);
|
||||
});
|
||||
|
||||
it("has the expected state when expanding a node", async () => {
|
||||
const { wrapper, store } = mount({}, {});
|
||||
|
||||
expect(formatObjectInspector(wrapper)).toMatchSnapshot();
|
||||
let nodes = wrapper.find(".node");
|
||||
const root1 = nodes.at(0);
|
||||
|
||||
let onPropertiesLoad = waitForDispatch(store, "NODE_PROPERTIES_LOADED");
|
||||
root1.simulate("click");
|
||||
await onPropertiesLoad;
|
||||
wrapper.update();
|
||||
|
||||
expect(storeHasLoadedProperty(store, "root-1")).toBeTruthy();
|
||||
// We don't want to track root actors.
|
||||
expect(
|
||||
getActors(store.getState()).has(
|
||||
gripRepStubs.get("testMoreThanMaxProps").actor
|
||||
)
|
||||
).toBeFalsy();
|
||||
expect(formatObjectInspector(wrapper)).toMatchSnapshot();
|
||||
|
||||
nodes = wrapper.find(".node");
|
||||
const protoNode = nodes.at(1);
|
||||
|
||||
onPropertiesLoad = waitForDispatch(store, "NODE_PROPERTIES_LOADED");
|
||||
protoNode.simulate("click");
|
||||
await onPropertiesLoad;
|
||||
wrapper.update();
|
||||
|
||||
// Once all the loading promises are resolved, actors and loadedProperties
|
||||
// should have the expected values.
|
||||
expect(formatObjectInspector(wrapper)).toMatchSnapshot();
|
||||
expect(
|
||||
storeHasLoadedProperty(store, "Symbol(root-1/<prototype>)")
|
||||
).toBeTruthy();
|
||||
|
||||
expect(
|
||||
getActors(store.getState()).has(protoStub.prototype.actor)
|
||||
).toBeTruthy();
|
||||
});
|
||||
|
||||
xit("has the expected state when expanding a proxy node", async () => {
|
||||
const { wrapper, store } = mount({});
|
||||
|
||||
expect(formatObjectInspector(wrapper)).toMatchSnapshot();
|
||||
let nodes = wrapper.find(".node");
|
||||
|
||||
const proxyNode = nodes.at(1);
|
||||
|
||||
let onLoadProperties = waitForDispatch(store, "NODE_PROPERTIES_LOADED");
|
||||
proxyNode.simulate("click");
|
||||
await onLoadProperties;
|
||||
wrapper.update();
|
||||
|
||||
// Once the properties are loaded, actors and loadedProperties should have
|
||||
// the expected values.
|
||||
expect(formatObjectInspector(wrapper)).toMatchSnapshot();
|
||||
|
||||
// We don't want to track root actors.
|
||||
expect(
|
||||
getActors(store.getState()).has(gripRepStubs.get("testProxy").actor)
|
||||
).toBeFalsy();
|
||||
|
||||
nodes = wrapper.find(".node");
|
||||
const handlerNode = nodes.at(3);
|
||||
onLoadProperties = waitForDispatch(store, "NODE_PROPERTIES_LOADED");
|
||||
handlerNode.simulate("click");
|
||||
await onLoadProperties;
|
||||
wrapper.update();
|
||||
|
||||
expect(formatObjectInspector(wrapper)).toMatchSnapshot();
|
||||
expect(
|
||||
storeHasLoadedProperty(store, "Symbol(root-2/<handler>)")
|
||||
).toBeTruthy();
|
||||
});
|
||||
|
||||
it("does not expand if the user selected some text", async () => {
|
||||
const { wrapper, store } = mount(
|
||||
{},
|
||||
{
|
||||
initialSate: {
|
||||
loadedProperties: new Map([
|
||||
["root-1", gripPropertiesStubs.get("proto-properties-symbols")]
|
||||
])
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
expect(formatObjectInspector(wrapper)).toMatchSnapshot();
|
||||
const nodes = wrapper.find(".node");
|
||||
|
||||
// Set a selection using the mock.
|
||||
getSelection().setMockSelection("test");
|
||||
|
||||
const root1 = nodes.at(0);
|
||||
root1.simulate("click");
|
||||
expect(storeHasExpandedPath(store, "root-1")).toBeFalsy();
|
||||
expect(formatObjectInspector(wrapper)).toMatchSnapshot();
|
||||
|
||||
// Clear the selection for other tests.
|
||||
getSelection().setMockSelection();
|
||||
});
|
||||
|
||||
it("expands if user selected some text and clicked the arrow", async () => {
|
||||
const { wrapper, store } = mount(
|
||||
{},
|
||||
{
|
||||
initialState: {
|
||||
loadedProperties: new Map([
|
||||
["root-1", gripPropertiesStubs.get("proto-properties-symbols")]
|
||||
])
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
expect(formatObjectInspector(wrapper)).toMatchSnapshot();
|
||||
const nodes = wrapper.find(".node");
|
||||
|
||||
// Set a selection using the mock.
|
||||
getSelection().setMockSelection("test");
|
||||
|
||||
const root1 = nodes.at(0);
|
||||
root1.find(".arrow").simulate("click");
|
||||
expect(getExpandedPaths(store.getState()).has("root-1")).toBeTruthy();
|
||||
expect(formatObjectInspector(wrapper)).toMatchSnapshot();
|
||||
|
||||
// Clear the selection for other tests.
|
||||
getSelection().setMockSelection();
|
||||
});
|
||||
|
||||
it("does not throw when expanding a block node", async () => {
|
||||
const blockNode = createNode({
|
||||
name: "Block",
|
||||
contents: [
|
||||
{
|
||||
name: "a",
|
||||
contents: {
|
||||
value: 30
|
||||
}
|
||||
},
|
||||
{
|
||||
name: "b",
|
||||
contents: {
|
||||
value: 32
|
||||
}
|
||||
}
|
||||
],
|
||||
type: NODE_TYPES.BLOCK
|
||||
});
|
||||
|
||||
const proxyNode = createNode({
|
||||
name: "Proxy",
|
||||
contents: {
|
||||
value: gripRepStubs.get("testProxy")
|
||||
}
|
||||
});
|
||||
|
||||
const { wrapper, store } = mount({
|
||||
roots: [blockNode, proxyNode]
|
||||
});
|
||||
|
||||
expect(formatObjectInspector(wrapper)).toMatchSnapshot();
|
||||
|
||||
const nodes = wrapper.find(".node");
|
||||
const root = nodes.at(0);
|
||||
const onPropertiesLoaded = waitForDispatch(store, "NODE_PROPERTIES_LOADED");
|
||||
root.simulate("click");
|
||||
await onPropertiesLoaded;
|
||||
wrapper.update();
|
||||
|
||||
expect(formatObjectInspector(wrapper)).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("calls recordTelemetryEvent when expanding a node", async () => {
|
||||
const recordTelemetryEvent = jest.fn();
|
||||
const { wrapper, store } = mount(
|
||||
{
|
||||
recordTelemetryEvent
|
||||
},
|
||||
{
|
||||
initialState: {
|
||||
loadedProperties: new Map([
|
||||
["root-1", gripPropertiesStubs.get("proto-properties-symbols")]
|
||||
])
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
let nodes = wrapper.find(".node");
|
||||
const root1 = nodes.at(0);
|
||||
const root2 = nodes.at(1);
|
||||
|
||||
// Expanding a node calls recordTelemetryEvent.
|
||||
root1.simulate("click");
|
||||
expect(recordTelemetryEvent.mock.calls).toHaveLength(1);
|
||||
expect(recordTelemetryEvent.mock.calls[0][0]).toEqual("object_expanded");
|
||||
|
||||
// Collapsing a node does not call recordTelemetryEvent.
|
||||
root1.simulate("click");
|
||||
expect(recordTelemetryEvent.mock.calls).toHaveLength(1);
|
||||
|
||||
// Expanding another node calls recordTelemetryEvent.
|
||||
const onPropertiesLoaded = waitForDispatch(store, "NODE_PROPERTIES_LOADED");
|
||||
root2.simulate("click");
|
||||
await onPropertiesLoaded;
|
||||
expect(recordTelemetryEvent.mock.calls).toHaveLength(2);
|
||||
expect(recordTelemetryEvent.mock.calls[1][0]).toEqual("object_expanded");
|
||||
|
||||
wrapper.update();
|
||||
|
||||
// Re-expanding a node calls recordTelemetryEvent.
|
||||
root1.simulate("click");
|
||||
expect(recordTelemetryEvent.mock.calls).toHaveLength(3);
|
||||
expect(recordTelemetryEvent.mock.calls[2][0]).toEqual("object_expanded");
|
||||
|
||||
nodes = wrapper.find(".node");
|
||||
const propNode = nodes.at(1);
|
||||
const symbolNode = nodes.at(2);
|
||||
const protoNode = nodes.at(3);
|
||||
|
||||
propNode.simulate("click");
|
||||
symbolNode.simulate("click");
|
||||
protoNode.simulate("click");
|
||||
|
||||
// The property and symbols have primitive values, and can't be expanded.
|
||||
expect(recordTelemetryEvent.mock.calls).toHaveLength(4);
|
||||
expect(recordTelemetryEvent.mock.calls[3][0]).toEqual("object_expanded");
|
||||
});
|
||||
|
||||
it("expanding a getter returning a longString does not throw", async () => {
|
||||
const { wrapper, store } = mount(
|
||||
{
|
||||
focusable: false
|
||||
},
|
||||
{
|
||||
initialState: {
|
||||
loadedProperties: new Map([
|
||||
["root-1", gripPropertiesStubs.get("longs-string-safe-getter")]
|
||||
])
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
wrapper
|
||||
.find(".node")
|
||||
.at(0)
|
||||
.simulate("click");
|
||||
wrapper.update();
|
||||
|
||||
const onPropertiesLoaded = waitForDispatch(store, "NODE_PROPERTIES_LOADED");
|
||||
wrapper
|
||||
.find(".node")
|
||||
.at(1)
|
||||
.simulate("click");
|
||||
await onPropertiesLoaded;
|
||||
|
||||
wrapper.update();
|
||||
expect(formatObjectInspector(wrapper)).toMatchSnapshot();
|
||||
});
|
||||
});
|
|
@ -0,0 +1,81 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
|
||||
|
||||
const { mountObjectInspector } = require("../test-utils");
|
||||
const { MODE } = require("../../../reps/constants");
|
||||
const { createNode } = require("../../utils/node");
|
||||
|
||||
const functionStubs = require("../../../reps/stubs/function");
|
||||
const ObjectClient = require("../__mocks__/object-client");
|
||||
|
||||
function generateDefaults(overrides) {
|
||||
return {
|
||||
autoExpandDepth: 1,
|
||||
...overrides
|
||||
};
|
||||
}
|
||||
|
||||
function mount(props) {
|
||||
const client = { createObjectClient: grip => ObjectClient(grip) };
|
||||
|
||||
return mountObjectInspector({
|
||||
client,
|
||||
props: generateDefaults(props)
|
||||
});
|
||||
}
|
||||
|
||||
describe("ObjectInspector - functions", () => {
|
||||
it("renders named function properties as expected", () => {
|
||||
const stub = functionStubs.get("Named");
|
||||
const { wrapper } = mount({
|
||||
roots: [
|
||||
createNode({
|
||||
name: "fn",
|
||||
contents: { value: stub }
|
||||
})
|
||||
]
|
||||
});
|
||||
|
||||
const nodes = wrapper.find(".node");
|
||||
const functionNode = nodes.first();
|
||||
expect(functionNode.text()).toBe("fn:testName()");
|
||||
});
|
||||
|
||||
it("renders anon function properties as expected", () => {
|
||||
const stub = functionStubs.get("Anon");
|
||||
const { wrapper } = mount({
|
||||
roots: [
|
||||
createNode({
|
||||
name: "fn",
|
||||
contents: { value: stub }
|
||||
})
|
||||
]
|
||||
});
|
||||
|
||||
const nodes = wrapper.find(".node");
|
||||
const functionNode = nodes.first();
|
||||
// It should have the name of the property.
|
||||
expect(functionNode.text()).toBe("fn()");
|
||||
});
|
||||
|
||||
it("renders non-TINY mode functions as expected", () => {
|
||||
const stub = functionStubs.get("Named");
|
||||
const { wrapper } = mount({
|
||||
autoExpandDepth: 0,
|
||||
roots: [
|
||||
{
|
||||
path: "root",
|
||||
name: "x",
|
||||
contents: { value: stub }
|
||||
}
|
||||
],
|
||||
mode: MODE.LONG
|
||||
});
|
||||
|
||||
const nodes = wrapper.find(".node");
|
||||
const functionNode = nodes.first();
|
||||
// It should have the name of the property.
|
||||
expect(functionNode.text()).toBe("x: function testName()");
|
||||
});
|
||||
});
|
|
@ -0,0 +1,102 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
|
||||
|
||||
const { MODE } = require("../../../reps/constants");
|
||||
const {
|
||||
formatObjectInspector,
|
||||
waitForLoadedProperties,
|
||||
mountObjectInspector
|
||||
} = require("../test-utils");
|
||||
|
||||
const { makeNodesForProperties } = require("../../utils/node");
|
||||
const accessorStubs = require("../../../reps/stubs/accessor");
|
||||
const ObjectClient = require("../__mocks__/object-client");
|
||||
|
||||
function generateDefaults(overrides) {
|
||||
return {
|
||||
autoExpandDepth: 1,
|
||||
createObjectClient: grip => ObjectClient(grip),
|
||||
mode: MODE.LONG,
|
||||
...overrides
|
||||
};
|
||||
}
|
||||
|
||||
function mount(stub, propsOverride = {}) {
|
||||
const client = { createObjectClient: grip => ObjectClient(grip) };
|
||||
|
||||
const root = { path: "root", name: "root" };
|
||||
const nodes = makeNodesForProperties(
|
||||
{
|
||||
ownProperties: {
|
||||
x: stub
|
||||
}
|
||||
},
|
||||
root
|
||||
);
|
||||
root.contents = nodes;
|
||||
|
||||
return mountObjectInspector({
|
||||
client,
|
||||
props: generateDefaults({ roots: [root], ...propsOverride })
|
||||
});
|
||||
}
|
||||
|
||||
describe("ObjectInspector - getters & setters", () => {
|
||||
it("renders getters as expected", async () => {
|
||||
const { store, wrapper } = mount(accessorStubs.get("getter"));
|
||||
await waitForLoadedProperties(store, ["root"]);
|
||||
wrapper.update();
|
||||
|
||||
expect(formatObjectInspector(wrapper)).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("renders setters as expected", async () => {
|
||||
const { store, wrapper } = mount(accessorStubs.get("setter"));
|
||||
await waitForLoadedProperties(store, ["root"]);
|
||||
wrapper.update();
|
||||
|
||||
expect(formatObjectInspector(wrapper)).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("renders getters and setters as expected", async () => {
|
||||
const { store, wrapper } = mount(accessorStubs.get("getter setter"));
|
||||
await waitForLoadedProperties(store, ["root"]);
|
||||
wrapper.update();
|
||||
|
||||
expect(formatObjectInspector(wrapper)).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("onInvokeGetterButtonClick + getter", async () => {
|
||||
const onInvokeGetterButtonClick = jest.fn();
|
||||
const { store, wrapper } = mount(accessorStubs.get("getter"), {
|
||||
onInvokeGetterButtonClick
|
||||
});
|
||||
await waitForLoadedProperties(store, ["root"]);
|
||||
wrapper.update();
|
||||
|
||||
expect(formatObjectInspector(wrapper)).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("onInvokeGetterButtonClick + setter", async () => {
|
||||
const onInvokeGetterButtonClick = jest.fn();
|
||||
const { store, wrapper } = mount(accessorStubs.get("setter"), {
|
||||
onInvokeGetterButtonClick
|
||||
});
|
||||
await waitForLoadedProperties(store, ["root"]);
|
||||
wrapper.update();
|
||||
|
||||
expect(formatObjectInspector(wrapper)).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("onInvokeGetterButtonClick + getter & setter", async () => {
|
||||
const onInvokeGetterButtonClick = jest.fn();
|
||||
const { store, wrapper } = mount(accessorStubs.get("getter setter"), {
|
||||
onInvokeGetterButtonClick
|
||||
});
|
||||
await waitForLoadedProperties(store, ["root"]);
|
||||
wrapper.update();
|
||||
|
||||
expect(formatObjectInspector(wrapper)).toMatchSnapshot();
|
||||
});
|
||||
});
|
|
@ -0,0 +1,82 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
|
||||
|
||||
const { mountObjectInspector } = require("../test-utils");
|
||||
const repsPath = "../../../reps";
|
||||
const { MODE } = require(`${repsPath}/constants`);
|
||||
|
||||
const { formatObjectInspector, waitForDispatch } = require("../test-utils");
|
||||
const ObjectClient = require("../__mocks__/object-client");
|
||||
const gripRepStubs = require(`${repsPath}/stubs/grip`);
|
||||
|
||||
function generateDefaults(overrides) {
|
||||
return {
|
||||
autoExpandDepth: 0,
|
||||
mode: MODE.LONG,
|
||||
...overrides
|
||||
};
|
||||
}
|
||||
|
||||
function mount(props) {
|
||||
const client = { createObjectClient: grip => ObjectClient(grip) };
|
||||
|
||||
return mountObjectInspector({
|
||||
client,
|
||||
props: generateDefaults(props)
|
||||
});
|
||||
}
|
||||
|
||||
describe("ObjectInspector - keyboard navigation", () => {
|
||||
it("works as expected", async () => {
|
||||
const stub = gripRepStubs.get("testMaxProps");
|
||||
|
||||
const { wrapper, store } = mount({
|
||||
roots: [{ path: "root", contents: { value: stub } }]
|
||||
});
|
||||
|
||||
expect(formatObjectInspector(wrapper)).toMatchSnapshot();
|
||||
|
||||
wrapper.simulate("focus");
|
||||
expect(formatObjectInspector(wrapper)).toMatchSnapshot();
|
||||
|
||||
// Pressing right arrow key should expand the node and lod its properties.
|
||||
const onPropertiesLoaded = waitForDispatch(store, "NODE_PROPERTIES_LOADED");
|
||||
simulateKeyDown(wrapper, "ArrowRight");
|
||||
await onPropertiesLoaded;
|
||||
wrapper.update();
|
||||
expect(formatObjectInspector(wrapper)).toMatchSnapshot();
|
||||
|
||||
// The child node should be focused.
|
||||
keyNavigate(wrapper, store, "ArrowDown");
|
||||
expect(formatObjectInspector(wrapper)).toMatchSnapshot();
|
||||
|
||||
// The root node should be focused again.
|
||||
keyNavigate(wrapper, store, "ArrowLeft");
|
||||
expect(formatObjectInspector(wrapper)).toMatchSnapshot();
|
||||
|
||||
// The child node should be focused again.
|
||||
keyNavigate(wrapper, store, "ArrowRight");
|
||||
expect(formatObjectInspector(wrapper)).toMatchSnapshot();
|
||||
|
||||
// The root node should be focused again.
|
||||
keyNavigate(wrapper, store, "ArrowUp");
|
||||
expect(formatObjectInspector(wrapper)).toMatchSnapshot();
|
||||
|
||||
wrapper.simulate("blur");
|
||||
expect(formatObjectInspector(wrapper)).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
||||
function keyNavigate(wrapper, store, key) {
|
||||
simulateKeyDown(wrapper, key);
|
||||
wrapper.update();
|
||||
}
|
||||
|
||||
function simulateKeyDown(wrapper, key) {
|
||||
wrapper.simulate("keydown", {
|
||||
key,
|
||||
preventDefault: () => {},
|
||||
stopPropagation: () => {}
|
||||
});
|
||||
}
|
|
@ -0,0 +1,155 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
|
||||
|
||||
/* global jest */
|
||||
|
||||
const { mountObjectInspector } = require("../test-utils");
|
||||
const gripRepStubs = require("../../../reps/stubs/grip");
|
||||
const ObjectClient = require("../__mocks__/object-client");
|
||||
|
||||
const { formatObjectInspector } = require("../test-utils");
|
||||
|
||||
function generateDefaults(overrides) {
|
||||
return {
|
||||
autoExpandDepth: 0,
|
||||
createObjectClient: grip => ObjectClient(grip),
|
||||
...overrides
|
||||
};
|
||||
}
|
||||
|
||||
function getEnumPropertiesMock() {
|
||||
return jest.fn(() => ({
|
||||
iterator: {
|
||||
slice: () => ({})
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
function mount(props, { initialState } = {}) {
|
||||
const enumProperties = getEnumPropertiesMock();
|
||||
|
||||
const client = {
|
||||
createObjectClient: grip => ObjectClient(grip, { enumProperties })
|
||||
};
|
||||
|
||||
const obj = mountObjectInspector({
|
||||
client,
|
||||
props: generateDefaults(props),
|
||||
initialState
|
||||
});
|
||||
|
||||
return { ...obj, enumProperties };
|
||||
}
|
||||
describe("ObjectInspector - properties", () => {
|
||||
it("does not load properties if properties are already loaded", () => {
|
||||
const stub = gripRepStubs.get("testMaxProps");
|
||||
|
||||
const { enumProperties } = mount(
|
||||
{
|
||||
autoExpandDepth: 1,
|
||||
roots: [
|
||||
{
|
||||
path: "root",
|
||||
contents: {
|
||||
value: stub
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
initialState: {
|
||||
objectInspector: {
|
||||
loadedProperties: new Map([
|
||||
["root", { ownProperties: stub.preview.ownProperties }]
|
||||
]),
|
||||
evaluations: new Map()
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
expect(enumProperties.mock.calls).toHaveLength(0);
|
||||
});
|
||||
|
||||
it("calls enumProperties when expandable leaf is clicked", () => {
|
||||
const stub = gripRepStubs.get("testMaxProps");
|
||||
const { enumProperties, wrapper } = mount({
|
||||
roots: [
|
||||
{
|
||||
path: "root",
|
||||
contents: {
|
||||
value: stub
|
||||
}
|
||||
}
|
||||
],
|
||||
createObjectClient: grip => ObjectClient(grip, { enumProperties })
|
||||
});
|
||||
|
||||
const node = wrapper.find(".node");
|
||||
node.simulate("click");
|
||||
|
||||
// The function is called twice, to get both non-indexed and indexed props.
|
||||
expect(enumProperties.mock.calls).toHaveLength(2);
|
||||
expect(enumProperties.mock.calls[0][0]).toEqual({
|
||||
ignoreNonIndexedProperties: true
|
||||
});
|
||||
expect(enumProperties.mock.calls[1][0]).toEqual({
|
||||
ignoreIndexedProperties: true
|
||||
});
|
||||
});
|
||||
|
||||
it("renders uninitialized bindings", () => {
|
||||
const { wrapper } = mount({
|
||||
roots: [
|
||||
{
|
||||
name: "someFoo",
|
||||
path: "root/someFoo",
|
||||
contents: {
|
||||
value: {
|
||||
uninitialized: true
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
expect(formatObjectInspector(wrapper)).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("renders unmapped bindings", () => {
|
||||
const { wrapper } = mount({
|
||||
roots: [
|
||||
{
|
||||
name: "someFoo",
|
||||
path: "root/someFoo",
|
||||
contents: {
|
||||
value: {
|
||||
unmapped: true
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
expect(formatObjectInspector(wrapper)).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("renders unscoped bindings", () => {
|
||||
const { wrapper } = mount({
|
||||
roots: [
|
||||
{
|
||||
name: "someFoo",
|
||||
path: "root/someFoo",
|
||||
contents: {
|
||||
value: {
|
||||
unscoped: true
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
expect(formatObjectInspector(wrapper)).toMatchSnapshot();
|
||||
});
|
||||
});
|
|
@ -0,0 +1,115 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
|
||||
|
||||
/* global jest */
|
||||
const { mountObjectInspector } = require("../test-utils");
|
||||
|
||||
const { MODE } = require("../../../reps/constants");
|
||||
const stub = require("../../../reps/stubs/grip").get("testProxy");
|
||||
const { formatObjectInspector } = require("../test-utils");
|
||||
|
||||
const ObjectClient = require("../__mocks__/object-client");
|
||||
function generateDefaults(overrides) {
|
||||
return {
|
||||
roots: [
|
||||
{
|
||||
path: "root",
|
||||
contents: { value: stub }
|
||||
}
|
||||
],
|
||||
autoExpandDepth: 1,
|
||||
mode: MODE.LONG,
|
||||
...overrides
|
||||
};
|
||||
}
|
||||
|
||||
function getEnumPropertiesMock() {
|
||||
return jest.fn(() => ({
|
||||
iterator: {
|
||||
slice: () => ({})
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
function mount(props, { initialState } = {}) {
|
||||
const enumProperties = getEnumPropertiesMock();
|
||||
|
||||
const client = {
|
||||
createObjectClient: grip => ObjectClient(grip, { enumProperties })
|
||||
};
|
||||
|
||||
const obj = mountObjectInspector({
|
||||
client,
|
||||
props: generateDefaults(props),
|
||||
initialState
|
||||
});
|
||||
|
||||
return { ...obj, enumProperties };
|
||||
}
|
||||
|
||||
describe("ObjectInspector - Proxy", () => {
|
||||
it("renders Proxy as expected", () => {
|
||||
const { wrapper, enumProperties } = mount(
|
||||
{},
|
||||
{
|
||||
initialState: {
|
||||
objectInspector: {
|
||||
// Have the prototype already loaded so the component does not call
|
||||
// enumProperties for the root's properties.
|
||||
loadedProperties: new Map([["root", { prototype: {} }]]),
|
||||
evaluations: new Map()
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
expect(formatObjectInspector(wrapper)).toMatchSnapshot();
|
||||
|
||||
// enumProperties should not have been called.
|
||||
expect(enumProperties.mock.calls).toHaveLength(0);
|
||||
});
|
||||
|
||||
it("calls enumProperties on <target> and <handler> clicks", () => {
|
||||
const { wrapper, enumProperties } = mount(
|
||||
{},
|
||||
{
|
||||
initialState: {
|
||||
objectInspector: {
|
||||
// Have the prototype already loaded so the component does not call
|
||||
// enumProperties for the root's properties.
|
||||
loadedProperties: new Map([["root", { prototype: {} }]]),
|
||||
evaluations: new Map()
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
const nodes = wrapper.find(".node");
|
||||
|
||||
const targetNode = nodes.at(1);
|
||||
const handlerNode = nodes.at(2);
|
||||
|
||||
targetNode.simulate("click");
|
||||
// The function is called twice,
|
||||
// to get both non-indexed and indexed properties.
|
||||
expect(enumProperties.mock.calls).toHaveLength(2);
|
||||
expect(enumProperties.mock.calls[0][0]).toEqual({
|
||||
ignoreNonIndexedProperties: true
|
||||
});
|
||||
expect(enumProperties.mock.calls[1][0]).toEqual({
|
||||
ignoreIndexedProperties: true
|
||||
});
|
||||
|
||||
handlerNode.simulate("click");
|
||||
// The function is called twice,
|
||||
// to get both non-indexed and indexed properties.
|
||||
expect(enumProperties.mock.calls).toHaveLength(4);
|
||||
expect(enumProperties.mock.calls[2][0]).toEqual({
|
||||
ignoreNonIndexedProperties: true
|
||||
});
|
||||
expect(enumProperties.mock.calls[3][0]).toEqual({
|
||||
ignoreIndexedProperties: true
|
||||
});
|
||||
});
|
||||
});
|
|
@ -0,0 +1,107 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
|
||||
|
||||
/* global jest */
|
||||
const { mountObjectInspector } = require("../test-utils");
|
||||
|
||||
const repsPath = "../../../reps";
|
||||
const gripRepStubs = require(`${repsPath}/stubs/grip`);
|
||||
const ObjectClient = require("../__mocks__/object-client");
|
||||
const stub = gripRepStubs.get("testMoreThanMaxProps");
|
||||
const { waitForDispatch } = require("../test-utils");
|
||||
|
||||
function getEnumPropertiesMock() {
|
||||
return jest.fn(() => ({
|
||||
iterator: {
|
||||
slice: () => ({})
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
function generateDefaults(overrides) {
|
||||
return {
|
||||
autoExpandDepth: 0,
|
||||
roots: [
|
||||
{
|
||||
path: "root",
|
||||
contents: {
|
||||
value: stub
|
||||
}
|
||||
}
|
||||
],
|
||||
...overrides
|
||||
};
|
||||
}
|
||||
|
||||
function mount(props, { initialState } = {}) {
|
||||
const enumProperties = getEnumPropertiesMock();
|
||||
|
||||
const client = {
|
||||
createObjectClient: grip => ObjectClient(grip, { enumProperties }),
|
||||
releaseActor: jest.fn()
|
||||
};
|
||||
|
||||
return mountObjectInspector({
|
||||
client,
|
||||
props: generateDefaults(props),
|
||||
initialState: {
|
||||
objectInspector: {
|
||||
...initialState,
|
||||
evaluations: new Map()
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
describe("release actors", () => {
|
||||
it("calls release actors when unmount", () => {
|
||||
const { wrapper, client } = mount(
|
||||
{},
|
||||
{
|
||||
initialState: {
|
||||
actors: new Set(["actor 1", "actor 2"])
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
wrapper.unmount();
|
||||
|
||||
expect(client.releaseActor.mock.calls).toHaveLength(2);
|
||||
expect(client.releaseActor.mock.calls[0][0]).toBe("actor 1");
|
||||
expect(client.releaseActor.mock.calls[1][0]).toBe("actor 2");
|
||||
});
|
||||
|
||||
it.skip("calls release actors when the roots prop changed", async () => {
|
||||
const { wrapper, store, client } = mount(
|
||||
{
|
||||
injectWaitService: true
|
||||
},
|
||||
{
|
||||
initialState: {
|
||||
actors: new Set(["actor 3", "actor 4"])
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
const onRootsChanged = waitForDispatch(store, "ROOTS_CHANGED");
|
||||
|
||||
wrapper.setProps({
|
||||
roots: [
|
||||
{
|
||||
path: "root-2",
|
||||
contents: {
|
||||
value: gripRepStubs.get("testMaxProps")
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
wrapper.update();
|
||||
//
|
||||
await onRootsChanged;
|
||||
//
|
||||
expect(client.releaseActor.mock.calls).toHaveLength(2);
|
||||
expect(client.releaseActor.mock.calls[0][0]).toBe("actor 3");
|
||||
expect(client.releaseActor.mock.calls[1][0]).toBe("actor 4");
|
||||
});
|
||||
});
|
|
@ -0,0 +1,88 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
|
||||
|
||||
const { createNode } = require("../../utils/node");
|
||||
const { waitForDispatch, mountObjectInspector } = require("../test-utils");
|
||||
|
||||
const gripWindowStubs = require("../../../reps/stubs/window");
|
||||
const ObjectClient = require("../__mocks__/object-client");
|
||||
const windowNode = createNode({
|
||||
name: "window",
|
||||
contents: { value: gripWindowStubs.get("Window") }
|
||||
});
|
||||
|
||||
const client = { createObjectClient: grip => ObjectClient(grip) };
|
||||
|
||||
function generateDefaults(overrides) {
|
||||
return {
|
||||
autoExpandDepth: 0,
|
||||
roots: [windowNode],
|
||||
...overrides
|
||||
};
|
||||
}
|
||||
|
||||
describe("ObjectInspector - dimTopLevelWindow", () => {
|
||||
it("renders window as expected when dimTopLevelWindow is true", async () => {
|
||||
const props = generateDefaults({
|
||||
dimTopLevelWindow: true
|
||||
});
|
||||
|
||||
const { wrapper, store } = mountObjectInspector({ client, props });
|
||||
let nodes = wrapper.find(".node");
|
||||
const node = nodes.at(0);
|
||||
|
||||
expect(nodes.at(0).hasClass("lessen")).toBeTruthy();
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
|
||||
const onPropertiesLoaded = waitForDispatch(store, "NODE_PROPERTIES_LOADED");
|
||||
node.simulate("click");
|
||||
await onPropertiesLoaded;
|
||||
wrapper.update();
|
||||
|
||||
nodes = wrapper.find(".node");
|
||||
expect(nodes.at(0).hasClass("lessen")).toBeFalsy();
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("renders collapsed top-level window when dimTopLevelWindow =false", () => {
|
||||
// The window node should not have the "lessen" class when
|
||||
// dimTopLevelWindow is falsy.
|
||||
const props = generateDefaults();
|
||||
const { wrapper } = mountObjectInspector({ client, props });
|
||||
|
||||
expect(wrapper.find(".node.lessen").exists()).toBeFalsy();
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("renders sub-level window", async () => {
|
||||
// The window node should not have the "lessen" class when it is not at
|
||||
// top level.
|
||||
const root = createNode({
|
||||
name: "root",
|
||||
contents: [windowNode]
|
||||
});
|
||||
|
||||
const props = generateDefaults({
|
||||
roots: [root],
|
||||
dimTopLevelWindow: true,
|
||||
injectWaitService: true
|
||||
});
|
||||
const { wrapper, store } = mountObjectInspector({ client, props });
|
||||
|
||||
let nodes = wrapper.find(".node");
|
||||
const node = nodes.at(0);
|
||||
const onPropertiesLoaded = waitForDispatch(store, "NODE_PROPERTIES_LOADED");
|
||||
node.simulate("click");
|
||||
await onPropertiesLoaded;
|
||||
wrapper.update();
|
||||
|
||||
nodes = wrapper.find(".node");
|
||||
const win = nodes.at(1);
|
||||
|
||||
// Make sure we target the window object.
|
||||
expect(win.find(".objectBox-Window").exists()).toBeTruthy();
|
||||
expect(win.hasClass("lessen")).toBeFalsy();
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
});
|
||||
});
|
|
@ -0,0 +1,253 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
|
||||
|
||||
// @flow
|
||||
import type { Store } from "../types";
|
||||
const { mount } = require("enzyme");
|
||||
const React = require("react");
|
||||
|
||||
const { createFactory } = React;
|
||||
|
||||
const { Provider } = require("react-redux");
|
||||
const { combineReducers } = require("redux");
|
||||
|
||||
const { thunk } = require("../../shared/redux/middleware/thunk");
|
||||
const {
|
||||
waitUntilService
|
||||
} = require("../../shared/redux/middleware/waitUntilService");
|
||||
|
||||
/**
|
||||
* Redux store utils
|
||||
* @module utils/create-store
|
||||
*/
|
||||
|
||||
import { createStore, applyMiddleware } from "redux";
|
||||
|
||||
const objectInspector = require("../index");
|
||||
const {
|
||||
getLoadedProperties,
|
||||
getLoadedPropertyKeys,
|
||||
getExpandedPaths,
|
||||
getExpandedPathKeys
|
||||
} = require("../reducer");
|
||||
|
||||
const ObjectInspector = createFactory(objectInspector.ObjectInspector);
|
||||
|
||||
const {
|
||||
WAIT_UNTIL_TYPE
|
||||
} = require("../../shared/redux/middleware/waitUntilService");
|
||||
|
||||
/*
|
||||
* Takes an Enzyme wrapper (obtained with mount/shallow/…) and
|
||||
* returns a stringified version of the ObjectInspector, e.g.
|
||||
*
|
||||
* ▼ Map { Symbol(a) → "value-a", Symbol(b) → "value-b" }
|
||||
* | size : 2
|
||||
* | ▼ <entries>
|
||||
* | | ▼ 0 : Symbol(a) → "value-a"
|
||||
* | | | <key> : Symbol(a)
|
||||
* | | | <value> : "value-a"
|
||||
* | | ▼ 1 : Symbol(b) → "value-b"
|
||||
* | | | <key> : Symbol(b)
|
||||
* | | | <value> : "value-b"
|
||||
* | ▼ <prototype> : Object { … }
|
||||
*
|
||||
*/
|
||||
function formatObjectInspector(wrapper: Object) {
|
||||
const hasFocusedNode = wrapper.find(".tree-node.focused").length > 0;
|
||||
const textTree = wrapper
|
||||
.find(".tree-node")
|
||||
.map(node => {
|
||||
const indentStr = "| ".repeat((node.prop("aria-level") || 1) - 1);
|
||||
// Need to target .arrow or Enzyme will also match the ArrowExpander
|
||||
// component.
|
||||
const arrow = node.find(".arrow");
|
||||
let arrowStr = " ";
|
||||
if (arrow.exists()) {
|
||||
arrowStr = arrow.hasClass("expanded") ? "▼ " : "▶︎ ";
|
||||
} else {
|
||||
arrowStr = " ";
|
||||
}
|
||||
|
||||
const icon = node
|
||||
.find(".node")
|
||||
.first()
|
||||
.hasClass("block")
|
||||
? "☲ "
|
||||
: "";
|
||||
let text = `${indentStr}${arrowStr}${icon}${getSanitizedNodeText(node)}`;
|
||||
|
||||
if (node.find("button.invoke-getter").exists()) {
|
||||
text = `${text}(>>)`;
|
||||
}
|
||||
|
||||
if (!hasFocusedNode) {
|
||||
return text;
|
||||
}
|
||||
return node.hasClass("focused") ? `[ ${text} ]` : ` ${text}`;
|
||||
})
|
||||
.join("\n");
|
||||
// Wrap the text representation in new lines so it keeps alignment between
|
||||
// tree nodes.
|
||||
return `\n${textTree}\n`;
|
||||
}
|
||||
|
||||
function getSanitizedNodeText(node) {
|
||||
// Stripping off the invisible space used in the indent.
|
||||
return node.text().replace(/^\u200B+/, "");
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait for a specific action type to be dispatched.
|
||||
*
|
||||
* @param {Object} store: Redux store
|
||||
* @param {String} type: type of the actin to wait for
|
||||
* @return {Promise}
|
||||
*/
|
||||
function waitForDispatch(
|
||||
store: Object,
|
||||
type: string
|
||||
): Promise<{ type: string }> {
|
||||
return new Promise(resolve => {
|
||||
store.dispatch({
|
||||
type: WAIT_UNTIL_TYPE,
|
||||
predicate: action => action.type === type,
|
||||
run: (dispatch, getState, action) => {
|
||||
resolve(action);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait until the condition evaluates to something truthy
|
||||
* @param {function} condition: function that we need for returning something
|
||||
* truthy.
|
||||
* @param {int} interval: Time to wait before trying to evaluate condition again
|
||||
* @param {int} maxTries: Number of evaluation to try.
|
||||
*/
|
||||
async function waitFor(
|
||||
condition: any => any,
|
||||
interval: number = 50,
|
||||
maxTries: number = 100
|
||||
) {
|
||||
let res = condition();
|
||||
while (!res) {
|
||||
await new Promise(done => setTimeout(done, interval));
|
||||
maxTries--;
|
||||
|
||||
if (maxTries <= 0) {
|
||||
throw new Error("waitFor - maxTries limit hit");
|
||||
}
|
||||
|
||||
res = condition();
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait until the state has all the expected keys for the loadedProperties
|
||||
* state prop.
|
||||
* @param {Redux Store} store: function that we need for returning something
|
||||
* truthy.
|
||||
* @param {Array} expectedKeys: Array of stringified keys.
|
||||
* @param {int} interval: Time to wait before trying to evaluate condition again
|
||||
* @param {int} maxTries: Number of evaluation to try.
|
||||
*/
|
||||
function waitForLoadedProperties(
|
||||
store: Store,
|
||||
expectedKeys: Array<string>,
|
||||
interval: number,
|
||||
maxTries: number
|
||||
): Promise<any> {
|
||||
return waitFor(
|
||||
() => storeHasLoadedPropertiesKeys(store, expectedKeys),
|
||||
interval,
|
||||
maxTries
|
||||
);
|
||||
}
|
||||
|
||||
function storeHasLoadedPropertiesKeys(
|
||||
store: Store,
|
||||
expectedKeys: Array<string>
|
||||
) {
|
||||
return expectedKeys.every(key => storeHasLoadedProperty(store, key));
|
||||
}
|
||||
|
||||
function storeHasLoadedProperty(store: Store, key: string): boolean {
|
||||
return getLoadedPropertyKeys(store.getState()).some(
|
||||
k => k.toString() === key
|
||||
);
|
||||
}
|
||||
|
||||
function storeHasExactLoadedProperties(
|
||||
store: Store,
|
||||
expectedKeys: Array<string>
|
||||
) {
|
||||
return (
|
||||
expectedKeys.length === getLoadedProperties(store.getState()).size &&
|
||||
expectedKeys.every(key => storeHasLoadedProperty(store, key))
|
||||
);
|
||||
}
|
||||
|
||||
function storeHasExpandedPaths(store: Store, expectedKeys: Array<string>) {
|
||||
return expectedKeys.every(key => storeHasExpandedPath(store, key));
|
||||
}
|
||||
|
||||
function storeHasExpandedPath(store: Store, key: string): boolean {
|
||||
return getExpandedPathKeys(store.getState()).some(k => k.toString() === key);
|
||||
}
|
||||
|
||||
function storeHasExactExpandedPaths(store: Store, expectedKeys: Array<string>) {
|
||||
return (
|
||||
expectedKeys.length === getExpandedPaths(store.getState()).size &&
|
||||
expectedKeys.every(key => storeHasExpandedPath(store, key))
|
||||
);
|
||||
}
|
||||
|
||||
function createOiStore(client: any, initialState: any = {}) {
|
||||
const reducers = { objectInspector: objectInspector.reducer.default };
|
||||
return configureStore({
|
||||
thunkArgs: args => ({ ...args, client })
|
||||
})(combineReducers(reducers), initialState);
|
||||
}
|
||||
|
||||
const configureStore = (opts: ReduxStoreOptions = {}) => {
|
||||
const middleware = [thunk(opts.thunkArgs), waitUntilService];
|
||||
return applyMiddleware(...middleware)(createStore);
|
||||
};
|
||||
|
||||
function mountObjectInspector({ props, client, initialState = {} }) {
|
||||
if (initialState.objectInspector) {
|
||||
initialState.objectInspector = {
|
||||
expandedPaths: new Set(),
|
||||
loadedProperties: new Map(),
|
||||
actors: new Set(),
|
||||
...initialState.objectInspector
|
||||
};
|
||||
}
|
||||
const store = createOiStore(client, initialState);
|
||||
const wrapper = mount(
|
||||
createFactory(Provider)({ store }, ObjectInspector(props))
|
||||
);
|
||||
|
||||
const tree = wrapper.find(".tree");
|
||||
|
||||
return { store, tree, wrapper, client };
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
formatObjectInspector,
|
||||
storeHasExpandedPaths,
|
||||
storeHasExpandedPath,
|
||||
storeHasExactExpandedPaths,
|
||||
storeHasLoadedPropertiesKeys,
|
||||
storeHasLoadedProperty,
|
||||
storeHasExactLoadedProperties,
|
||||
waitFor,
|
||||
waitForDispatch,
|
||||
waitForLoadedProperties,
|
||||
mountObjectInspector,
|
||||
createStore: createOiStore
|
||||
};
|
|
@ -0,0 +1,58 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`promises utils function makeNodesForPromiseProperties 1`] = `
|
||||
Array [
|
||||
Object {
|
||||
"contents": Object {
|
||||
"value": "rejected",
|
||||
},
|
||||
"meta": undefined,
|
||||
"name": "<state>",
|
||||
"parent": Object {
|
||||
"contents": Object {
|
||||
"value": Object {
|
||||
"actor": "server2.conn2.child1/pausedobj36",
|
||||
"class": "Promise",
|
||||
"promiseState": Object {
|
||||
"reason": Object {
|
||||
"type": "3",
|
||||
},
|
||||
"state": "rejected",
|
||||
},
|
||||
"type": "object",
|
||||
},
|
||||
},
|
||||
"path": "root",
|
||||
},
|
||||
"path": Symbol(root/<state>),
|
||||
"type": Symbol(<state>),
|
||||
},
|
||||
Object {
|
||||
"contents": Object {
|
||||
"value": Object {
|
||||
"type": "3",
|
||||
},
|
||||
},
|
||||
"meta": undefined,
|
||||
"name": "<reason>",
|
||||
"parent": Object {
|
||||
"contents": Object {
|
||||
"value": Object {
|
||||
"actor": "server2.conn2.child1/pausedobj36",
|
||||
"class": "Promise",
|
||||
"promiseState": Object {
|
||||
"reason": Object {
|
||||
"type": "3",
|
||||
},
|
||||
"state": "rejected",
|
||||
},
|
||||
"type": "object",
|
||||
},
|
||||
},
|
||||
"path": "root",
|
||||
},
|
||||
"path": Symbol(root/<reason>),
|
||||
"type": Symbol(<reason>),
|
||||
},
|
||||
]
|
||||
`;
|
|
@ -0,0 +1,87 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
|
||||
|
||||
const { createNode, NODE_TYPES } = require("../../utils/node");
|
||||
|
||||
describe("createNode", () => {
|
||||
it("returns null when contents is undefined", () => {
|
||||
expect(createNode({ name: "name" })).toBeNull();
|
||||
});
|
||||
|
||||
it("does not return null when contents is null", () => {
|
||||
expect(
|
||||
createNode({
|
||||
name: "name",
|
||||
path: "path",
|
||||
contents: null
|
||||
})
|
||||
).not.toBe(null);
|
||||
});
|
||||
|
||||
it("returns the expected object when parent is undefined", () => {
|
||||
const node = createNode({
|
||||
name: "name",
|
||||
path: "path",
|
||||
contents: "contents"
|
||||
});
|
||||
expect(node).toEqual({
|
||||
name: "name",
|
||||
path: node.path,
|
||||
contents: "contents",
|
||||
type: NODE_TYPES.GRIP
|
||||
});
|
||||
});
|
||||
|
||||
it("returns the expected object when parent is not null", () => {
|
||||
const root = createNode({ name: "name", contents: null });
|
||||
const child = createNode({
|
||||
parent: root,
|
||||
name: "name",
|
||||
path: "path",
|
||||
contents: "contents"
|
||||
});
|
||||
expect(child.parent).toEqual(root);
|
||||
});
|
||||
|
||||
it("returns the expected object when type is not undefined", () => {
|
||||
const root = createNode({ name: "name", contents: null });
|
||||
const child = createNode({
|
||||
parent: root,
|
||||
name: "name",
|
||||
path: "path",
|
||||
contents: "contents",
|
||||
type: NODE_TYPES.BUCKET
|
||||
});
|
||||
|
||||
expect(child.type).toEqual(NODE_TYPES.BUCKET);
|
||||
});
|
||||
|
||||
it("uses the name property for the path when path is not provided", () => {
|
||||
expect(
|
||||
createNode({ name: "name", contents: "contents" }).path.toString()
|
||||
).toBe("Symbol(name)");
|
||||
});
|
||||
|
||||
it("wraps the path in a Symbol when provided", () => {
|
||||
expect(
|
||||
createNode({
|
||||
name: "name",
|
||||
path: "path",
|
||||
contents: "contents"
|
||||
}).path.toString()
|
||||
).toBe("Symbol(path)");
|
||||
});
|
||||
|
||||
it("uses parent path to compute its path", () => {
|
||||
const root = createNode({ name: "root", contents: null });
|
||||
expect(
|
||||
createNode({
|
||||
parent: root,
|
||||
name: "name",
|
||||
path: "path",
|
||||
contents: "contents"
|
||||
}).path.toString()
|
||||
).toBe("Symbol(root/path)");
|
||||
});
|
||||
});
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче