├── .gitignore ├── .travis.yml ├── .vscode └── settings.json ├── CHANGELOG.md ├── README.md ├── RELEASE.md ├── after.png ├── before.png ├── index.js ├── index.test.js ├── jest.config.js ├── jest.setup.js ├── manual-testing ├── example-dom-fixture.js ├── jest.test.js └── node.js ├── package-lock.json ├── package.json └── src ├── .npmignore ├── JsonMLElement.js ├── __snapshots__ ├── body.test.js.snap └── header.test.js.snap ├── body.js ├── body.test.js ├── findInlineText.js ├── findInlineText.test.js ├── hasBody.js ├── hasBody.test.js ├── header.js ├── header.test.js ├── isElement.js └── styles.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "lts/*" 4 | - "8" 5 | notifications: 6 | email: false 7 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "files.insertFinalNewline": true 3 | } 4 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to this project will be documented in this file. 4 | This project adheres to [Semantic Versioning](http://semver.org/) and [keepachangelog.com](http://keepachangelog.com/). 5 | 6 | ## [unreleased] 7 | 8 | ## [1.0.1] - 2022-12-29 9 | 10 | ### Fixed 11 | 12 | - Updated internal tests and examples to latest versions, NodeJS LTS/v18 and Jest v29 13 | 14 | ## [1.0.0] - 2018-04-09 15 | 16 | - Release under new home (jsdom org) 17 | 18 | ## [0.2.0] - 2018-04-01 19 | 20 | ### Added 21 | 22 | - `.uninstall` function 23 | 24 | ### Changed 25 | 26 | - Fix [#2](https://github.com/jsdom/jsdom-devtools-formatter/issues/2) _readme screenshots would look nicer as PNGs, instead of JPEG_ 27 | 28 | ## [0.1.0] - 2018-03-06 29 | 30 | ### Added 31 | 32 | - First release, yay! 33 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # jsdom-devtools-formatter [![Build Status](https://travis-ci.org/jsdom/jsdom-devtools-formatter.svg?branch=master)](https://travis-ci.org/jsdom/jsdom-devtools-formatter) 2 | 3 | In a nutshell: Instead of trying to understand what [jsdom](https://github.com/jsdom/jsdom)'s elements represents by inspecting their _implementation_ objects like so: 4 | ![before.png](before.png) 5 | 6 | 7 | …let's just inspect them like they were real HTML elements: 8 | ![after.png](after.png) 9 | 10 | Typical use-case would be some script/test that utilizes [jsdom](https://github.com/jsdom/jsdom) in a Node.js environment, e.g. [Jest](https://jestjs.io/). 11 | 12 | 13 | ## How to use 14 | 15 | ```bash 16 | npm install jsdom-devtools-formatter 17 | ``` 18 | 19 | ```js 20 | // in some file.js 21 | const jsdomDevtoolsFormatter = require('jsdom-devtools-formatter'); 22 | jsdomDevtoolsFormatter.install(); 23 | ``` 24 | 25 | ```js 26 | // You can also opt-out at some later point by: 27 | jsdomDevtoolsFormatter.uninstall(); 28 | ``` 29 | 30 | E.g. for Jest you need to install the formatter in a [`setupFilesAfterEnv` configuration file](https://jestjs.io/docs/configuration#setupfilesafterenv-array). 31 | 32 | As a one-time thing also need to: 33 | - Open Chrome's Devtools 34 | - Click the "⠇" in the upper-right corner > Settings 35 | - Under "Console", check _"Enable custom formatters"_ 36 | 37 | 38 | ## Development 39 | 40 | The source code is all plain vanilla JS and standard CommonJS modules. 41 | Tests are written using [Jest](https://jestjs.io/) 42 | 43 | See package.json's `scripts` sections for all available commands. The most useul ones are probably: 44 | 45 | ### Automated Tests 46 | 47 | ```bash 48 | # run all tests once: 49 | npm test 50 | 51 | # run tests in "watch mode" 52 | npm test -- --watch 53 | ``` 54 | 55 | 56 | ### Manual Testing 57 | 58 | In addition to verifying _logical changes_ using the automated tests, it's recommended to verify that things "look & feel" as expected using the _manual_ tests: 59 | 60 | - Open [chrome://inspect](chrome://inspect) and click the _"Open dedicated DevTools for Node"_ link 61 | - Then run: 62 | 63 | ```bash 64 | npm run test:manual_node 65 | # -or- 66 | npm run test:manual_jest 67 | ``` 68 | 69 | It should stop at the `debugger` call site, from there you can follow the inlined comment with instructions to play with the console output. 70 | 71 | 72 | ## Related resources 73 | 74 | - https://github.com/jsdom/jsdom 75 | - [Custom Object Formatters in Chrome DevTools (gdoc)](https://bit.ly/object-formatters) 76 | - [Contributing to Chrome DevTools (gdoc)](https://bit.ly/devtools-contribution-guide) 77 | - [Debugging Node.js Apps](https://nodejs.org/en/docs/inspector/) 78 | - [Debugger, Advanced usage, V8 Inspector Integration for Node.js](https://nodejs.org/dist/latest-v18.x/docs/api/debugger.html#v8-inspector-integration-for-nodejs) 79 | -------------------------------------------------------------------------------- /RELEASE.md: -------------------------------------------------------------------------------- 1 | - update changelog (+commit) 2 | - npm version 3 | - npm publish 4 | -------------------------------------------------------------------------------- /after.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsdom/jsdom-devtools-formatter/f9073a205263f78b5c8782dcb33080124e100651/after.png -------------------------------------------------------------------------------- /before.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsdom/jsdom-devtools-formatter/f9073a205263f78b5c8782dcb33080124e100651/before.png -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | const header = require('./src/header'); 2 | const body = require('./src/body'); 3 | const hasBody = require('./src/hasBody'); 4 | 5 | const formatter = { 6 | header, 7 | body, 8 | hasBody, 9 | 10 | install: () => { 11 | global.devtoolsFormatters = global.devtoolsFormatters || []; 12 | if (global.devtoolsFormatters.indexOf(formatter) === -1) { 13 | global.devtoolsFormatters.push(formatter); 14 | } 15 | }, 16 | 17 | uninstall: () => { 18 | global.devtoolsFormatters = global.devtoolsFormatters || []; 19 | global.devtoolsFormatters = global.devtoolsFormatters.filter(x => x !== formatter); 20 | } 21 | }; 22 | 23 | module.exports = formatter; 24 | -------------------------------------------------------------------------------- /index.test.js: -------------------------------------------------------------------------------- 1 | const formatter = require('./index'); 2 | const header = require('./src/header'); 3 | const body = require('./src/body'); 4 | const hasBody = require('./src/hasBody'); 5 | 6 | describe('formatter', () => { 7 | it('has expected functions', () => { 8 | expect(formatter.header).toBe(header); 9 | expect(formatter.hasBody).toBe(hasBody); 10 | expect(formatter.body).toBe(body); 11 | }) 12 | }); 13 | 14 | describe('.install', () => { 15 | afterEach(() => { 16 | delete global.devtoolsFormatters; 17 | }); 18 | 19 | it('setup formatter as expected', () => { 20 | formatter.install(); 21 | expect(global.devtoolsFormatters).toEqual([formatter]); 22 | 23 | // only set it up once 24 | formatter.install(); 25 | expect(global.devtoolsFormatters).toEqual([formatter]); 26 | }) 27 | 28 | describe('.uninstall', () => { 29 | it('removes formatter as expected', () => { 30 | formatter.uninstall(); 31 | expect(global.devtoolsFormatters).toEqual([]); 32 | 33 | // idempotent 34 | formatter.uninstall(); 35 | expect(global.devtoolsFormatters).toEqual([]); 36 | }) 37 | }) 38 | }) 39 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | testEnvironment: "jsdom", 3 | setupFilesAfterEnv: ['./jest.setup.js'], 4 | }; 5 | -------------------------------------------------------------------------------- /jest.setup.js: -------------------------------------------------------------------------------- 1 | const jsdomDevtoolsFormatter = require("./index") 2 | jsdomDevtoolsFormatter.install(); 3 | -------------------------------------------------------------------------------- /manual-testing/example-dom-fixture.js: -------------------------------------------------------------------------------- 1 | module.exports = ` 2 |
3 |

4 | An example of formatting 5 |

6 | 7 | 8 | 9 |
10 |

11 | The formatter should output shorter texts inline 12 |

13 |

14 | Somewhat longer text have to be expanded to be visible 15 |

16 |

17 | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. 18 |

19 |
20 |

21 | Hello world! 22 |

23 |
24 | 25 | and some random text outside elements 26 | `.trim() 27 | -------------------------------------------------------------------------------- /manual-testing/jest.test.js: -------------------------------------------------------------------------------- 1 | const exampleDomFixture = require('./example-dom-fixture'); 2 | 3 | test("manual", () => { 4 | document.body.innerHTML = exampleDomFixture; 5 | 6 | let exampleNode = document.getElementById("root"); 7 | expect(exampleNode).toBeDefined(); 8 | 9 | debugger; 10 | // type exampleNode in the console, 11 | // it should be rendered like an actual HTML element which can be expanded and explored 12 | }); 13 | -------------------------------------------------------------------------------- /manual-testing/node.js: -------------------------------------------------------------------------------- 1 | const exampleDomFixture = require('./example-dom-fixture'); 2 | 3 | const jsdomDevtoolsFormatter = require("../index") 4 | jsdomDevtoolsFormatter.install(); 5 | 6 | const { JSDOM } = require("jsdom"); 7 | const dom = new JSDOM(`${exampleDomFixture}`); 8 | const { window } = dom; 9 | const { document } = window; 10 | 11 | let exampleNode = document.getElementById("root"); 12 | debugger; 13 | // type exampleNode in the console, 14 | // it should be rendered like an actual HTML element which can be expanded and explored 15 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "jsdom-devtools-formatter", 3 | "version": "1.0.1", 4 | "description": "Make jsdom elements look like real DOM elements in Chrome Devtools console", 5 | "repository": "https://github.com/jsdom/jsdom-devtools-formatter", 6 | "engines": { 7 | "node": ">=18.12.1" 8 | }, 9 | "main": "index.js", 10 | "files": [ 11 | "index.js", 12 | "src" 13 | ], 14 | "scripts": { 15 | "test": "jest", 16 | "test:debug": "node --inspect ./node_modules/.bin/jest --runInBand", 17 | "test:manual_jest": "node --inspect ./node_modules/.bin/jest --runInBand manual-testing/jest", 18 | "test:manual_node": "node --inspect manual-testing/node.js" 19 | }, 20 | "keywords": [ 21 | "jsdom", 22 | "chrome", 23 | "devtools", 24 | "console", 25 | "formatter" 26 | ], 27 | "author": "Nicklas Gummesson", 28 | "license": "MIT", 29 | "devDependencies": { 30 | "jest": "^29.3.1", 31 | "jest-environment-jsdom": "^29.3.1", 32 | "jsdom": "^20.0.3" 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/.npmignore: -------------------------------------------------------------------------------- 1 | __snapshots__ 2 | *.test.js 3 | -------------------------------------------------------------------------------- /src/JsonMLElement.js: -------------------------------------------------------------------------------- 1 | JsonMLElement = function(tagName) { 2 | this._attributes = {}; 3 | this._jsonML = [tagName, this._attributes]; 4 | }; 5 | 6 | JsonMLElement.prototype = { 7 | createChild: function(tagName) { 8 | var c = new JsonMLElement(tagName); 9 | this._jsonML.push(c.toJsonML()); 10 | return c; 11 | }, 12 | 13 | createObjectTag: function(object) { 14 | var tag = this.createChild("object"); 15 | tag.setAttribute("object", object); 16 | return tag; 17 | }, 18 | 19 | setStyle: function(style) { 20 | this._attributes["style"] = style; 21 | }, 22 | 23 | setAttribute: function(key, value) { 24 | this._attributes[key] = value; 25 | }, 26 | 27 | createTextChild: function(text) { 28 | this._jsonML.push(text + ""); 29 | }, 30 | 31 | toJsonML: function() { 32 | return this._jsonML; 33 | } 34 | }; 35 | 36 | module.exports = JsonMLElement; 37 | -------------------------------------------------------------------------------- /src/__snapshots__/body.test.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`body returns expected JsonML for 1st-level elements 1`] = ` 4 | [ 5 | "ol", 6 | { 7 | "style": " 8 | list-style-type: none; 9 | margin: 2px 0 0 28px; 10 | padding: 0; 11 | ", 12 | }, 13 | [ 14 | "li", 15 | { 16 | "style": " 17 | margin-bottom: 2px; 18 | ", 19 | }, 20 | "", 21 | ], 22 | [ 23 | "li", 24 | { 25 | "style": " 26 | margin-bottom: 2px; 27 | ", 28 | }, 29 | [ 30 | "object", 31 | { 32 | "object":

35 | A title 36 |

, 37 | }, 38 | ], 39 | ], 40 | [ 41 | "li", 42 | { 43 | "style": " 44 | margin-bottom: 2px; 45 | ", 46 | }, 47 | "", 48 | ], 49 | [ 50 | "li", 51 | { 52 | "style": " 53 | margin-bottom: 2px; 54 | ", 55 | }, 56 | [ 57 | "object", 58 | { 59 | "object":
62 | 63 | 64 |

65 | Lorem ipsum 66 |

67 | 68 | 69 |

70 | and so forth 71 |

72 | 73 | 74 |
, 75 | }, 76 | ], 77 | ], 78 | [ 79 | "li", 80 | { 81 | "style": " 82 | margin-bottom: 2px; 83 | ", 84 | }, 85 | "", 86 | ], 87 | [ 88 | "li", 89 | { 90 | "style": " 91 | margin-bottom: 2px; 92 | ", 93 | }, 94 | [ 95 | "object", 96 | { 97 | "object":
100 | 101 | 102 | 103 | 104 | 105 |
, 106 | }, 107 | ], 108 | ], 109 | [ 110 | "li", 111 | { 112 | "style": " 113 | margin-bottom: 2px; 114 | ", 115 | }, 116 | "", 117 | ], 118 | [ 119 | "li", 120 | { 121 | "style": " 122 | margin-bottom: 2px; 123 | ", 124 | }, 125 | [ 126 | "object", 127 | { 128 | "object":
129 | 130 | Foo bar 131 | 132 |
, 133 | }, 134 | ], 135 | ], 136 | [ 137 | "li", 138 | { 139 | "style": " 140 | margin-bottom: 2px; 141 | ", 142 | }, 143 | "", 144 | ], 145 | ] 146 | `; 147 | -------------------------------------------------------------------------------- /src/__snapshots__/header.test.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`header returns a ellipsis for a complicated DOM element 1`] = ` 4 | [ 5 | "span", 6 | { 7 | "style": " 8 | color: rgb(168, 148, 166); 9 | ", 10 | }, 11 | "<", 12 | [ 13 | "span", 14 | { 15 | "style": " 16 | color: rgb(136, 18, 128); 17 | ", 18 | }, 19 | "div", 20 | ], 21 | " ", 22 | [ 23 | "span", 24 | { 25 | "style": " 26 | color: rgb(153, 69, 0); 27 | unicode-bidi: -webkit-isolate; 28 | ", 29 | }, 30 | "class", 31 | ], 32 | "="", 33 | [ 34 | "span", 35 | { 36 | "style": " 37 | color: rgb(26, 26, 166); 38 | unicode-bidi: -webkit-isolate; 39 | ", 40 | }, 41 | "foo bar baz", 42 | ], 43 | """, 44 | " ", 45 | [ 46 | "span", 47 | { 48 | "style": " 49 | color: rgb(153, 69, 0); 50 | unicode-bidi: -webkit-isolate; 51 | ", 52 | }, 53 | "id", 54 | ], 55 | "="", 56 | [ 57 | "span", 58 | { 59 | "style": " 60 | color: rgb(26, 26, 166); 61 | unicode-bidi: -webkit-isolate; 62 | ", 63 | }, 64 | "root", 65 | ], 66 | """, 67 | ">", 68 | [ 69 | "span", 70 | { 71 | "style": " 72 | color: black; 73 | unicode-bidi: -webkit-isolate; 74 | ", 75 | }, 76 | "…", 77 | ], 78 | "<", 79 | [ 80 | "span", 81 | { 82 | "style": " 83 | color: rgb(136, 18, 128); 84 | ", 85 | }, 86 | "/div", 87 | ], 88 | ">", 89 | ] 90 | `; 91 | 92 | exports[`header returns summary for a simple DOM element 1`] = ` 93 | [ 94 | "span", 95 | { 96 | "style": " 97 | color: rgb(168, 148, 166); 98 | ", 99 | }, 100 | "<", 101 | [ 102 | "span", 103 | { 104 | "style": " 105 | color: rgb(136, 18, 128); 106 | ", 107 | }, 108 | "div", 109 | ], 110 | ">", 111 | [ 112 | "span", 113 | { 114 | "style": " 115 | color: black; 116 | unicode-bidi: -webkit-isolate; 117 | ", 118 | }, 119 | "test text", 120 | ], 121 | "<", 122 | [ 123 | "span", 124 | { 125 | "style": " 126 | color: rgb(136, 18, 128); 127 | ", 128 | }, 129 | "/div", 130 | ], 131 | ">", 132 | ] 133 | `; 134 | -------------------------------------------------------------------------------- /src/body.js: -------------------------------------------------------------------------------- 1 | const JsonMLElement = require("./JsonMLElement"); 2 | const styles = require("./styles"); 3 | 4 | module.exports = x => { 5 | var body = new JsonMLElement("ol"); 6 | body.setStyle(styles.body); 7 | 8 | for (let i = 0; i < x.childNodes.length; i++) { 9 | const child = x.childNodes[i]; 10 | const item = body.createChild("li"); 11 | item.setStyle(styles.item); 12 | 13 | switch (child.nodeType) { 14 | case 1: // element 15 | item.createObjectTag(child); 16 | break; 17 | 18 | case 3: // text 19 | item.createTextChild(child.nodeValue.trim()); 20 | break; 21 | 22 | case 8: // comment 23 | item.createTextChild(``); 24 | item.setStyle(styles.comment); 25 | break; 26 | } 27 | } 28 | 29 | return body.toJsonML(); 30 | }; 31 | -------------------------------------------------------------------------------- /src/body.test.js: -------------------------------------------------------------------------------- 1 | const body = require("./body"); 2 | 3 | describe("body", () => { 4 | it("returns expected JsonML for 1st-level elements", () => { 5 | const element = document.createElement("div"); 6 | element.innerHTML = ` 7 |

A title

8 |
9 |

Lorem ipsum

10 |

and so forth

11 |
12 |
13 | 14 |
15 |
16 | Foo bar 17 |
18 | `; 19 | const actual = body(element); 20 | expect(actual).toMatchSnapshot(); 21 | }); 22 | }); 23 | -------------------------------------------------------------------------------- /src/findInlineText.js: -------------------------------------------------------------------------------- 1 | const isElement = require('./isElement'); 2 | const findInlineText = require('./findInlineText'); 3 | const isTextNode = x => x.nodeType === 3; 4 | 5 | module.exports = x => { 6 | if (!isElement(x)) return; 7 | 8 | const childNodes = Array.from(x.childNodes); 9 | if (!childNodes.every(isTextNode)) return; 10 | 11 | const str = childNodes.reduce((str, c) => str + c.nodeValue, ''); 12 | if (str.length <= 80) { 13 | return str.trim(); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/findInlineText.test.js: -------------------------------------------------------------------------------- 1 | const findInlineText = require('./findInlineText'); 2 | 3 | describe('findInlineText', () => { 4 | let element; 5 | 6 | beforeEach(() => { 7 | element = document.createElement('div'); 8 | }); 9 | 10 | it('returns inlined if text is short enough', () => { 11 | element.innerHTML = 'a'.repeat(50); 12 | expect(findInlineText(element)).toEqual(element.innerHTML); 13 | }); 14 | 15 | it('returns nothing if text is considered too long', () => { 16 | element.innerHTML = 'a'.repeat(100); 17 | expect(findInlineText(element)).toBeFalsy(); 18 | }); 19 | 20 | it('returns nothing for mixed content', () => { 21 | element.innerHTML = 'test
'; 22 | expect(findInlineText(element)).toBeFalsy(); 23 | }); 24 | 25 | it('returns nothing if contains a comment', () => { 26 | element.innerHTML = ''; 27 | expect(findInlineText(element)).toBeFalsy(); 28 | }); 29 | }); 30 | -------------------------------------------------------------------------------- /src/hasBody.js: -------------------------------------------------------------------------------- 1 | const isElement = require('./isElement'); 2 | const findInlineText = require('./findInlineText'); 3 | 4 | module.exports = x => isElement(x) && x.childNodes.length > 0 && !findInlineText(x) 5 | -------------------------------------------------------------------------------- /src/hasBody.test.js: -------------------------------------------------------------------------------- 1 | const hasBody = require("./hasBody"); 2 | 3 | describe("hasBody", () => { 4 | it('returns true for an element with at least a child element', () => { 5 | const element = document.createElement('div'); 6 | element.innerHTML = ` 7 | 8 | `; 9 | expect(hasBody(element)).toBe(true); 10 | }) 11 | 12 | it('returns false if element contains only text', () => { 13 | const element = document.createElement('div') 14 | element.innerHTML = 'a'.repeat(50); 15 | expect(hasBody(element)).toBe(false); 16 | }) 17 | 18 | it('returns true if element contains a long text', () => { 19 | const element = document.createElement('div') 20 | element.innerHTML = 'b'.repeat(100); 21 | expect(hasBody(element)).toBe(true); 22 | }) 23 | 24 | it("returns false for non-element input", () => { 25 | expect(hasBody()).toBe(false); 26 | expect(hasBody(null)).toBe(false); 27 | expect(hasBody(undefined)).toBe(false); 28 | expect(hasBody(false)).toBe(false); 29 | expect(hasBody("a string")).toBe(false); 30 | expect(hasBody(123)).toBe(false); 31 | expect(hasBody({})).toBe(false); 32 | expect(hasBody([])).toBe(false); 33 | expect(hasBody(new Date())).toBe(false); 34 | 35 | const element = document.createElement('div') 36 | expect(hasBody(element)).toBe(false); 37 | }); 38 | }); 39 | -------------------------------------------------------------------------------- /src/header.js: -------------------------------------------------------------------------------- 1 | const JsonMLElement = require('./JsonMLElement'); 2 | const styles = require('./styles'); 3 | const isElement = require('./isElement'); 4 | const findInlineText = require('./findInlineText'); 5 | 6 | module.exports = x => { 7 | if (!isElement(x)) { 8 | // i.e. use normal formatter 9 | return null; 10 | } 11 | 12 | const tagName = x.tagName.toLowerCase(); 13 | const el = new JsonMLElement("span"); 14 | el.setStyle(styles.tag); 15 | el.createTextChild("<"); 16 | 17 | const startTagName = el.createChild("span"); 18 | startTagName.createTextChild(tagName); 19 | startTagName.setStyle(styles.tagName); 20 | 21 | x.getAttributeNames().forEach(name => { 22 | el.createTextChild(" "); 23 | 24 | const attrName = el.createChild("span"); 25 | attrName.createTextChild(name); 26 | attrName.setStyle(styles.attributeName); 27 | el.createTextChild('="'); 28 | 29 | const attrVal = el.createChild("span"); 30 | attrVal.createTextChild(x.getAttribute(name)); 31 | attrVal.setStyle(styles.attributeValue); 32 | 33 | el.createTextChild('"'); 34 | }); 35 | 36 | el.createTextChild(">"); 37 | 38 | const content = findInlineText(x) || '…'; 39 | const text = el.createChild('span'); 40 | text.createTextChild(content); 41 | text.setStyle(styles.text); 42 | 43 | el.createTextChild("<"); 44 | 45 | const closeTagName = el.createChild("span"); 46 | closeTagName.setStyle(styles.tagName); 47 | closeTagName.createTextChild("/" + tagName); 48 | 49 | el.createTextChild(">"); 50 | return el.toJsonML(); 51 | }; 52 | -------------------------------------------------------------------------------- /src/header.test.js: -------------------------------------------------------------------------------- 1 | const header = require("./header"); 2 | 3 | describe("header", () => { 4 | it("returns null to utilize default formatter for a non-HTML elements", () => { 5 | expect(header()).toBe(null); 6 | expect(header(null)).toBe(null); 7 | expect(header(undefined)).toBe(null); 8 | expect(header(false)).toBe(null); 9 | expect(header("a string")).toBe(null); 10 | expect(header(123)).toBe(null); 11 | expect(header({})).toBe(null); 12 | expect(header([])).toBe(null); 13 | expect(header(new Date())).toBe(null); 14 | }); 15 | 16 | it("returns summary for a simple DOM element", () => { 17 | const element = document.createElement("div"); 18 | element.innerHTML = 'test text'; 19 | expect(header(element)).toMatchSnapshot(); 20 | }); 21 | 22 | it("returns a ellipsis for a complicated DOM element", () => { 23 | const element = document.createElement("div"); 24 | element.className = "foo bar baz"; 25 | element.id = "root"; 26 | element.innerHTML = ` 27 |

This is not part of the header

28 |

And this neither

29 | `; 30 | expect(header(element)).toMatchSnapshot(); 31 | }); 32 | }); 33 | -------------------------------------------------------------------------------- /src/isElement.js: -------------------------------------------------------------------------------- 1 | module.exports = x => !!x && typeof x === 'object' && x.nodeType === 1; 2 | -------------------------------------------------------------------------------- /src/styles.js: -------------------------------------------------------------------------------- 1 | // matches https://github.com/ChromeDevTools/devtools-frontend/blob/master/front_end/ui/inspectorSyntaxHighlight.css 2 | // webkit-html-* styles 3 | const defaultMargin = '2px'; 4 | module.exports = { 5 | tag: ` 6 | color: rgb(168, 148, 166); 7 | `, 8 | tagName: ` 9 | color: rgb(136, 18, 128); 10 | `, 11 | attributeName: ` 12 | color: rgb(153, 69, 0); 13 | unicode-bidi: -webkit-isolate; 14 | `, 15 | attributeValue: ` 16 | color: rgb(26, 26, 166); 17 | unicode-bidi: -webkit-isolate; 18 | `, 19 | body: ` 20 | list-style-type: none; 21 | margin: ${defaultMargin} 0 0 28px; 22 | padding: 0; 23 | `, 24 | item: ` 25 | margin-bottom: ${defaultMargin}; 26 | `, 27 | comment: ` 28 | color: rgb(35, 110, 37); 29 | `, 30 | text: ` 31 | color: black; 32 | unicode-bidi: -webkit-isolate; 33 | ` 34 | }; 35 | --------------------------------------------------------------------------------