From f7e2143af5984985277ef7dea7aa7e89b62fe46d Mon Sep 17 00:00:00 2001 From: Bing Dai Date: Mon, 18 Oct 2021 08:24:18 -0700 Subject: [PATCH] add highlight render option to Render Performance tab (#1685) * add highlight render option to Render Performance tab clean up * fix lint error and change per comment * Use bounds to find component instead of id * change per comment * change per comments * change per comments * change per comments * added test * replace timeout with later * use async * improve tests * add test for glimmer component, to show the highlight is not supported. * change per comments Co-authored-by: Bing Dai --- app/components/render-tree-toolbar.hbs | 13 + app/controllers/render-tree.js | 23 +- app/routes/render-tree.js | 34 +- app/templates/render-tree.hbs | 3 + ember_debug/models/profile-manager.js | 137 +++++- ember_debug/render-debug.js | 11 +- tests/ember_debug/profile-manager-test.js | 569 +++++++++++++++++++++- 7 files changed, 767 insertions(+), 23 deletions(-) diff --git a/app/components/render-tree-toolbar.hbs b/app/components/render-tree-toolbar.hbs index 145aa0b60f..d05db12c24 100644 --- a/app/components/render-tree-toolbar.hbs +++ b/app/components/render-tree-toolbar.hbs @@ -17,4 +17,17 @@ @value={{@searchValue}} class="js-render-profiles-search" /> + {{#if @isHighlightEnabled}} +
+ +
+ {{/if}} diff --git a/app/controllers/render-tree.js b/app/controllers/render-tree.js index 2f62ff2953..2b4c639b48 100644 --- a/app/controllers/render-tree.js +++ b/app/controllers/render-tree.js @@ -7,9 +7,12 @@ import debounceComputed from 'ember-inspector/computed/debounce'; import { and, equal } from '@ember/object/computed'; export default Controller.extend({ + port: service(), + initialEmpty: false, - modelEmpty: equal('model.length', 0), + modelEmpty: equal('model.profiles.length', 0), showEmpty: and('initialEmpty', 'modelEmpty'), + shouldHighlightRender: false, /** * Storage is needed for remembering if the user closed the warning @@ -56,16 +59,20 @@ export default Controller.extend({ return escapeRegExp(this.search.toLowerCase()); }), + isHighlightEnabled: computed('model.isHighlightSupported', function () { + return get(this.model, 'isHighlightSupported'); + }), + filtered: computed( 'escapedSearch', - 'model.@each.name', + 'model.profiles.@each.name', 'search', function () { if (isEmpty(this.escapedSearch)) { - return this.model; + return get(this.model, 'profiles'); } - return this.model.filter((item) => { + return get(this.model, 'profiles').filter((item) => { const regExp = new RegExp(this.escapedSearch); return recursiveMatch(item, regExp); }); @@ -75,6 +82,14 @@ export default Controller.extend({ closeWarning: action(function () { this.set('isWarningClosed', true); }), + + updateShouldHighlightRender: action(function () { + const value = !this.shouldHighlightRender; + this.set('shouldHighlightRender', value); + this.port.send('render:updateShouldHighlightRender', { + shouldHighlightRender: value, + }); + }), }); function recursiveMatch(item, regExp) { diff --git a/app/routes/render-tree.js b/app/routes/render-tree.js index 14fd85d691..fe0fdaf4a0 100644 --- a/app/routes/render-tree.js +++ b/app/routes/render-tree.js @@ -1,4 +1,4 @@ -import { action, get, set } from '@ember/object'; +import EmberObject, { action, get, set } from '@ember/object'; import { inject as service } from '@ember/service'; import { Promise } from 'rsvp'; import TabRoute from 'ember-inspector/routes/tab'; @@ -9,9 +9,12 @@ export default class RenderTreeRoute extends TabRoute { model() { const port = this.port; return new Promise(function (resolve) { - port.one('render:profilesAdded', function (message) { - resolve(message.profiles); - }); + port.one( + 'render:profilesAdded', + function ({ profiles, isHighlightSupported }) { + resolve(EmberObject.create({ profiles, isHighlightSupported })); + } + ); port.send('render:watchProfiles'); }); } @@ -19,7 +22,7 @@ export default class RenderTreeRoute extends TabRoute { setupController(controller, model) { super.setupController(...arguments); - if (model.length === 0) { + if (get(model, 'profiles.length') === 0) { controller.set('initialEmpty', true); } const port = this.port; @@ -37,16 +40,27 @@ export default class RenderTreeRoute extends TabRoute { } profilesUpdated(message) { - set(this, 'controller.model', message.profiles); + set(this, 'controller.model.profiles', message.profiles); } profilesAdded(message) { - const model = get(this, 'controller.model'); + const currentProfiles = get(this, 'controller.model.profiles'); const profiles = message.profiles; + if ( + message.isHighlightSupported !== undefined && + message.isHighlightSupported !== + get(this, 'controller.model.isHighlightSupported') + ) { + set( + this, + 'controller.model.isHighlightSupported', + message.isHighlightSupported + ); + } - model.pushObjects(profiles); - if (model.length > 100) { - set(this, 'controller.model', model.slice(0, 100)); + currentProfiles.pushObjects(profiles); + if (currentProfiles.length > 100) { + set(this, 'controller.model.profiles', currentProfiles.slice(0, 100)); } } diff --git a/app/templates/render-tree.hbs b/app/templates/render-tree.hbs index 57b7c971db..62a0223e11 100644 --- a/app/templates/render-tree.hbs +++ b/app/templates/render-tree.hbs @@ -5,6 +5,9 @@ @refreshPage={{action send "refreshPage"}} @searchValue={{this.searchValue}} @showEmpty={{this.showEmpty}} + @shouldHighlightRender={{this.shouldHighlightRender}} + @updateShouldHighlightRender={{this.updateShouldHighlightRender}} + @isHighlightEnabled={{this.isHighlightEnabled}} /> {{/in-element}} {{/if}} diff --git a/ember_debug/models/profile-manager.js b/ember_debug/models/profile-manager.js index a1debe00a7..7b76e39ab9 100644 --- a/ember_debug/models/profile-manager.js +++ b/ember_debug/models/profile-manager.js @@ -1,6 +1,65 @@ import ProfileNode from './profile-node'; +import Ember from '../utils/ember'; +import { compareVersion } from 'ember-debug/utils/version'; -import { later, scheduleOnce } from '../utils/ember/runloop'; +import { later, scheduleOnce, cancel } from '../utils/ember/runloop'; + +function getEdges(first, last, closest) { + let start = null; + let end = null; + for (let i = 0; i < closest.length; i++) { + if (closest.item(i) === first.node) start = i; + else if (closest.item(i) === last.node) end = i; + } + return [start, end]; +} + +function getUnfilteredRoots(first, last, closest) { + if (first.node === last.node) return [first.node]; + + const roots = []; + + const [start, end] = getEdges(first, last, closest); + + if (start === null || end === null) return []; + + for (let i = start; i <= end; i++) roots.push(closest.item(i)); + + return roots; +} + +function findRoots({ first, last, parent }) { + const closest = parent.childNodes; + + const roots = getUnfilteredRoots(first, last, closest); + + return roots.filter((el) => el?.nodeType === 1); +} + +function makeHighlight() { + const node = document.createElement('div'); + node.setAttribute('role', 'presentation'); + node.setAttribute('class', 'ember-inspector-render-highlight'); + return node; +} +function insertHTML(node) { + document.body.appendChild(node); +} + +function insertStylesheet() { + const content = ` + .ember-inspector-render-highlight { + border: 2px solid rgba(255,0,0,0.2); + box-shadow: 0px 0px 1px rgba(255,0,0,0.2); + z-index: 1000000; + pointer-events: none; + } + `; + const style = document.createElement('style'); + style.appendChild(document.createTextNode(content)); + document.head.appendChild(style); + return style; +} /** * A class for keeping track of active rendering profiles as a list. @@ -12,11 +71,20 @@ export default class ProfileManager { this.currentSet = []; this._profilesAddedCallbacks = []; this.queue = []; + this.shouldHighlightRender = false; + this.stylesheet = insertStylesheet(); + // keep track of all the active highlights + this.highlights = []; + this.isHighlightEnabled = compareVersion(Ember?.VERSION, '3.20.0') !== -1; } began(timestamp, payload, now) { return this.wrapForErrors(this, function () { this.current = new ProfileNode(timestamp, payload, this.current, now); + if (this.shouldHighlightRender && payload.view) { + this._highLightView(payload.view); + } + this.current.isHighlightEnabled = this.isHighlightEnabled; return this.current; }); } @@ -42,6 +110,18 @@ export default class ProfileManager { return callback.call(context); } + _highLightView(view) { + const symbols = Object.getOwnPropertySymbols(view); + const bounds = view[symbols.find((sym) => sym.description === 'BOUNDS')]; + if (!bounds) return; + + const elements = findRoots(bounds); + + elements.forEach((node) => { + this._renderHighlight(node); + }); + } + /** * Push a new profile into the queue * @param info @@ -77,6 +157,61 @@ export default class ProfileManager { } } + teardown() { + this.stylesheet?.remove(); + // remove all the active highlighted components + this._removeAllHighlights(); + } + + _removeAllHighlights() { + const els = this.highlights.slice(0); + els.forEach((el) => { + this._removeHighlight(el); + }); + } + + _removeHighlight(highlight) { + this.highlights = this.highlights.filter((item) => item !== highlight); + cancel(highlight.timeout); + highlight.el.remove(); + } + + _addHighlight(highlight) { + insertHTML(highlight.el); + this.highlights.push(highlight); + + highlight.timeout = later(() => { + this._removeHighlight(highlight); + }, 500); + } + + _constructHighlight(renderedNode) { + const rect = renderedNode.getBoundingClientRect(); + const highlight = makeHighlight(); + + const { top, left, width, height } = rect; + const { scrollX, scrollY } = window; + const { style } = highlight; + if (style) { + style.position = 'absolute'; + style.top = `${top + scrollY}px`; + style.left = `${left + scrollX}px`; + style.width = `${width}px`; + style.height = `${height}px`; + } + return highlight; + } + + _renderHighlight(renderedNode) { + if (!renderedNode?.getBoundingClientRect) { + return; + } + + const highlight = this._constructHighlight(renderedNode); + + this._addHighlight({ el: highlight }); + } + _flush() { let entry, i; for (i = 0; i < this.queue.length; i++) { diff --git a/ember_debug/render-debug.js b/ember_debug/render-debug.js index 9a1162255e..f32753a9e8 100644 --- a/ember_debug/render-debug.js +++ b/ember_debug/render-debug.js @@ -32,10 +32,14 @@ export default EmberObject.extend(PortMixin, { this.profileManager.offProfilesAdded(this, this.sendAdded); this.profileManager.offProfilesAdded(this, this._updateComponentTree); + this.profileManager.teardown(); }, sendAdded(profiles) { - this.sendMessage('profilesAdded', { profiles }); + this.sendMessage('profilesAdded', { + profiles, + isHighlightSupported: this.profileManager.isHighlightEnabled, + }); }, /** @@ -62,6 +66,10 @@ export default EmberObject.extend(PortMixin, { }); this.profileManager.onProfilesAdded(this, this.sendAdded); }, + + updateShouldHighlightRender({ shouldHighlightRender }) { + this.profileManager.shouldHighlightRender = shouldHighlightRender; + }, }, }); @@ -79,7 +87,6 @@ function _subscribeToRenderEvents() { payload, now: Date.now(), }; - return profileManager.addToQueue(info); }, diff --git a/tests/ember_debug/profile-manager-test.js b/tests/ember_debug/profile-manager-test.js index 44eadeaa12..4f64d7192d 100644 --- a/tests/ember_debug/profile-manager-test.js +++ b/tests/ember_debug/profile-manager-test.js @@ -1,8 +1,565 @@ -import { test } from 'qunit'; -import ProfileManager from 'ember-debug/models/profile-manager'; +import { find, visit, waitUntil, getSettledState } from '@ember/test-helpers'; +import EmberComponent from '@ember/component'; +import GlimmerComponent from '@glimmer/component'; +import EmberRoute from '@ember/routing/route'; +import Controller from '@ember/controller'; +import { module, test } from 'qunit'; +import { hbs } from 'ember-cli-htmlbars'; +import EmberDebug from 'ember-debug/main'; +import setupEmberDebugTest from '../helpers/setup-ember-debug-test'; +import { run } from '@ember/runloop'; +import Ember from 'ember-debug/utils/ember'; +import { compareVersion } from 'ember-debug/utils/version'; -test('Ember Debug - Construction', function (assert) { - let manager = new ProfileManager(); - assert.ok(!!manager, 'it was created'); - assert.equal(manager.profiles.length, 0, 'it has no profiles'); +const { VERSION } = Ember; + +const isComponentHighlightSupported = compareVersion(VERSION, '3.20.0') !== -1; + +const getRounded = (value) => { + let data = value; + if (typeof data === 'string') { + // remove unit px + if (data.indexOf('px') !== -1) { + data.replace('px', ''); + } + data = parseFloat(data); + } + return Math.floor(data); +}; +class OneRootGlimmer extends GlimmerComponent { + classNames = 'simple-component'; +} + +const mockedComponents = { + text: { + component: EmberComponent.extend({ + tagName: '', + }), + template: hbs('text only', { + moduleName: 'my-app/templates/components/text.hbs', + }), + }, + 'text-glimmer': { + component: GlimmerComponent, + template: hbs('text only', { + moduleName: 'my-app/templates/components/text-glimmer.hbs', + }), + }, + comment: { + component: EmberComponent.extend({ + tagName: '', + }), + template: hbs('', { + moduleName: 'my-app/templates/components/comment.hbs', + }), + }, + 'comment-glimmer': { + component: GlimmerComponent, + template: hbs('', { + moduleName: 'my-app/templates/components/comment-glimmer.hbs', + }), + }, + 'one-root': { + component: EmberComponent.extend({ + tagName: '', + }), + template: hbs('
one root
', { + moduleName: 'my-app/templates/components/one-root.hbs', + }), + }, + 'one-root-glimmer': { + component: OneRootGlimmer, + template: hbs('
one root
', { + moduleName: 'my-app/templates/components/one-root-glimmer.hbs', + }), + }, + 'two-root': { + component: EmberComponent.extend({ + tagName: '', + }), + template: hbs( + '
one
two
', + { moduleName: 'my-app/templates/components/two-root.hbs' } + ), + }, + 'two-root-glimmer': { + component: GlimmerComponent, + template: hbs( + '
one
two
', + { moduleName: 'my-app/templates/components/two-root-glimmer.hbs' } + ), + }, + 'root-comment-root': { + component: EmberComponent.extend({ + tagName: '', + }), + template: hbs( + '
one
two
', + { moduleName: 'my-app/templates/components/root-comment-root.hbs' } + ), + }, + 'root-comment-root-glimmer': { + component: GlimmerComponent, + template: hbs( + '
one
two
', + { + moduleName: 'my-app/templates/components/root-comment-root-glimmer.hbs', + } + ), + }, + 'comment-root-comment': { + component: EmberComponent.extend({ + tagName: '', + }), + template: hbs( + '
one
', + { moduleName: 'my-app/templates/components/comment-root-comment.hbs' } + ), + }, + 'comment-root-comment-glimmer': { + component: GlimmerComponent, + template: hbs( + '
one
', + { moduleName: 'my-app/templates/components/comment-root-comment.hbs' } + ), + }, + 'div-tag': { + component: EmberComponent.extend({ + classNames: ['simple-component'], + }), + template: hbs('text in div', { + moduleName: 'my-app/templates/components/div-tag.hbs', + }), + }, + 'div-roots': { + component: EmberComponent.extend({ + classNames: ['simple-component'], + }), + template: hbs('
one
two
', { + moduleName: 'my-app/templates/components/div-roots.hbs', + }), + }, +}; + +const mockedRoutes = { + 'text-route': { + template: hbs('', { + moduleName: 'my-app/templates/text-route.hbs', + }), + expectedRender: [], + }, + 'text-glimmer-route': { + template: hbs('', { + moduleName: 'my-app/templates/text-glimmer-route.hbs', + }), + expectedRender: [], + }, + 'comment-route': { + template: hbs('', { + moduleName: 'my-app/templates/comment-route.hbs', + }), + expectedRender: [], + }, + 'comment-glimmer-route': { + template: hbs('', { + moduleName: 'my-app/templates/comment-glimmer-route.hbs', + }), + expectedRender: [], + }, + 'one-root-route': { + template: hbs('', { + moduleName: 'my-app/templates/one-root-route.hbs', + }), + expectedRender: ['.simple-component'], + }, + 'one-root-glimmer-route': { + template: hbs('', { + moduleName: 'my-app/templates/one-root-glimmer-route.hbs', + }), + expectedRender: ['.simple-component'], + }, + 'two-root-route': { + template: hbs('', { + moduleName: 'my-app/templates/two-root-route.hbs', + }), + expectedRender: ['.simple-component', '.another-component'], + }, + 'two-root-glimmer-route': { + template: hbs('', { + moduleName: 'my-app/templates/two-root-glimmer-route.hbs', + }), + expectedRender: ['.simple-component', '.another-component'], + }, + 'root-comment-root-route': { + template: hbs('', { + moduleName: 'my-app/templates/root-comment-root-route.hbs', + }), + expectedRender: ['.simple-component', '.another-component'], + }, + 'root-comment-root-glimmer-route': { + template: hbs('', { + moduleName: 'my-app/templates/root-comment-root-glimmer-route.hbs', + }), + expectedRender: ['.simple-component', '.another-component'], + }, + 'comment-root-comment-route': { + template: hbs('', { + moduleName: 'my-app/templates/comment-root-comment-route.hbs', + }), + expectedRender: ['.simple-component'], + }, + 'comment-root-comment-glimmer-route': { + template: hbs('', { + moduleName: 'my-app/templates/comment-root-comment-glimmer-route.hbs', + }), + expectedRender: ['.simple-component'], + }, + 'div-tag-route': { + template: hbs('', { + moduleName: 'my-app/templates/div-tag-route.hbs', + }), + expectedRender: ['.simple-component'], + }, + 'div-roots-route': { + template: hbs('', { + moduleName: 'my-app/templates/div-roots-route.hbs', + }), + expectedRender: ['.simple-component'], + }, +}; + +const constructBase = (owner) => { + owner.register('route:application', EmberRoute); + + owner.register('controller:application', Controller); + + owner.register( + 'template:application', + hbs( + '
{{outlet}}
', + { moduleName: 'my-app/templates/application.hbs' } + ) + ); + + owner.register('route:home', EmberRoute); + + owner.register( + 'template:home', + hbs('Home', { moduleName: 'my-app/templates/home.hbs' }) + ); +}; + +const constructComponents = (owner, componentsMap) => { + for (const componentKey in componentsMap) { + if (componentsMap[componentKey].component) { + owner.register( + `component:${componentKey}`, + componentsMap[componentKey].component + ); + } + if (componentsMap[componentKey].template) { + owner.register( + `template:components/${componentKey}`, + componentsMap[componentKey].template + ); + } + } +}; + +const constructRoutes = (owner, routes) => { + routes.forEach((routeKey) => { + if (mockedRoutes[routeKey].route) { + owner.register(`route:${routeKey}`, mockedRoutes[routeKey].route); + } + if (mockedRoutes[routeKey].controller) { + owner.register( + `controller:${routeKey}`, + mockedRoutes[routeKey].controller + ); + } + if (mockedRoutes[routeKey].template) { + owner.register(`template:${routeKey}`, mockedRoutes[routeKey].template); + } + }); +}; + +const assertNodeSizes = (assert, synthetic, real) => { + const style = synthetic.style; + const box = real.getBoundingClientRect(); + const dimensions = [ + ['left', 'x'], + ['top', 'y'], + ['width', 'width'], + ['height', 'height'], + ]; + for (const [styleKey, boxKey] of dimensions) { + assert.equal( + getRounded(style[styleKey]), + getRounded(box[boxKey]), + `same ${boxKey} as component` + ); + } +}; + +const matchHighlights = ( + assert, + testedRoute, + newHighlights, + isGlimmerComponent +) => { + const renderedComponents = mockedRoutes[testedRoute].expectedRender.map( + (selector) => { + const component = find(selector); + assert.ok( + component, + isComponentHighlightSupported + ? 'expected component is rendered' + : 'expected component is rendered but the component highlight is not supported' + ); + return component; + } + ); + + if (isComponentHighlightSupported && !isGlimmerComponent) { + renderedComponents.forEach((renderedComponent, index) => { + assertNodeSizes(assert, newHighlights[index], renderedComponent); + }); + } else { + assert.notOk( + newHighlights.length, + 'Should not have any highlight if highlight is not supported' + ); + } +}; + +const enableHighlight = () => { + run(() => + EmberDebug.port.trigger('render:updateShouldHighlightRender', { + shouldHighlightRender: true, + }) + ); +}; + +async function highlightsPromise(testedRoute, isGlimmerComponent) { + await visit('/home'); + enableHighlight(); + // Glimmer component does support highlight. so there should not be any highlights + const numberOfHighlights = isGlimmerComponent + ? 0 + : mockedRoutes[testedRoute].expectedRender.length; + const observedHighlights = []; + if (!isComponentHighlightSupported) { + await visit('/' + testedRoute); + return observedHighlights; + } + const observer = new MutationObserver(function (records) { + records.forEach((record) => { + record.addedNodes.forEach((node) => { + if (node.className === 'ember-inspector-render-highlight') { + observedHighlights.push(node); + } + }); + }); + }); + observer.observe(document.body, { childList: true }); + await visit('/' + testedRoute); + if (numberOfHighlights > 0) { + await waitUntil(() => observedHighlights.length === numberOfHighlights, { + timeout: 2000, + }); + } else { + await waitUntil(() => { + // Check for the settled state minus hasPendingTimers + let { hasRunLoop, hasPendingRequests, hasPendingWaiters } = + getSettledState(); + if (hasRunLoop || hasPendingRequests || hasPendingWaiters) { + return false; + } + return true; + }); + } + observer.disconnect(); + return observedHighlights; +} + +module('Ember Debug - profile manager component highlight', function (hooks) { + setupEmberDebugTest(hooks, { + routes() { + this.route('home'); + Object.keys(mockedRoutes).forEach((route) => { + this.route(route); + }); + }, + }); + + hooks.beforeEach(async function () { + EmberDebug.IGNORE_DEPRECATIONS = true; + constructBase(this.owner); + constructComponents(this.owner, mockedComponents); + }); + + hooks.afterEach(function (assert) { + const highlights = document.getElementsByClassName( + 'ember-inspector-render-highlight' + ); + + assert.notOk( + highlights?.length, + 'highlights should be destroyed after execution' + ); + }); + + test('Should not show highlights for text component - Ember component', async function (assert) { + assert.expect(2); + + const testedRoute = 'text-route'; + constructRoutes(this.owner, [testedRoute]); + + const newHighlights = await highlightsPromise(testedRoute); + + assert.notOk(newHighlights.length, 'should not render highlight'); + }); + + test('Should not show highlights for text component - Glimmer component', async function (assert) { + assert.expect(2); + + const testedRoute = 'text-glimmer-route'; + constructRoutes(this.owner, [testedRoute]); + + const newHighlights = await highlightsPromise(testedRoute, true); + + assert.notOk(newHighlights.length, 'should not render highlight'); + }); + + test('Should not show highlights for comment component - Ember component', async function (assert) { + assert.expect(2); + + const testedRoute = 'comment-route'; + constructRoutes(this.owner, [testedRoute]); + + const newHighlights = await highlightsPromise(testedRoute); + + assert.notOk(newHighlights.length, 'should not render highlight'); + }); + + test('Should not show highlights for comment component - Glimmer component', async function (assert) { + assert.expect(2); + + const testedRoute = 'comment-glimmer-route'; + constructRoutes(this.owner, [testedRoute]); + + const newHighlights = await highlightsPromise(testedRoute, true); + + assert.notOk(newHighlights.length, 'should not render highlight'); + }); + + test('Should highlight one rootNode Ember component', async function (assert) { + assert.expect(isComponentHighlightSupported ? 6 : 3); + + const testedRoute = 'one-root-route'; + constructRoutes(this.owner, [testedRoute]); + + const newHighlights = await highlightsPromise(testedRoute); + + matchHighlights(assert, testedRoute, newHighlights); + }); + + test('Highlight is not supported, should not highlight one rootNode Glimmer component', async function (assert) { + assert.expect(3); + + const testedRoute = 'one-root-glimmer-route'; + constructRoutes(this.owner, [testedRoute]); + + const newHighlights = await highlightsPromise(testedRoute, true); + + matchHighlights(assert, testedRoute, newHighlights, true); + }); + + test('Should highlight two rootNode ([rootNode, rootNode] and no tagName) Ember component', async function (assert) { + assert.expect(isComponentHighlightSupported ? 11 : 4); + + const testedRoute = 'two-root-route'; + constructRoutes(this.owner, [testedRoute]); + + const newHighlights = await highlightsPromise(testedRoute); + + matchHighlights(assert, testedRoute, newHighlights); + }); + + test('Highlight is not supported, should not highlight two rootNode ([rootNode, rootNode] and no tagName) Glimmer component', async function (assert) { + assert.expect(4); + + const testedRoute = 'two-root-glimmer-route'; + constructRoutes(this.owner, [testedRoute]); + + const newHighlights = await highlightsPromise(testedRoute, true); + + matchHighlights(assert, testedRoute, newHighlights, true); + }); + + test('Should highlight two rootNode with one comment ([rootNode, commentNode, rootNode] and no tagName) Ember component', async function (assert) { + assert.expect(isComponentHighlightSupported ? 11 : 4); + + const testedRoute = 'root-comment-root-route'; + constructRoutes(this.owner, [testedRoute]); + + const newHighlights = await highlightsPromise(testedRoute); + + matchHighlights(assert, testedRoute, newHighlights); + }); + + test('Highlight is not supported, should not highlight two rootNode with one comment ([rootNode, commentNode, rootNode] and no tagName) Glimmer component', async function (assert) { + assert.expect(4); + + const testedRoute = 'root-comment-root-glimmer-route'; + constructRoutes(this.owner, [testedRoute]); + + const newHighlights = await highlightsPromise(testedRoute, true); + + matchHighlights(assert, testedRoute, newHighlights, true); + }); + + test('Should highlight one rootNode with two comment ([commentNode, rootNode, commentNode] and no tagName) Ember component', async function (assert) { + assert.expect(isComponentHighlightSupported ? 6 : 3); + + const testedRoute = 'comment-root-comment-route'; + constructRoutes(this.owner, [testedRoute]); + + const newHighlights = await highlightsPromise(testedRoute); + + matchHighlights(assert, testedRoute, newHighlights); + }); + + test('Highlight is not supported, should not highlight one rootNode with two comment ([commentNode, rootNode, commentNode] and no tagName) Glimmer component', async function (assert) { + assert.expect(3); + + const testedRoute = 'comment-root-comment-glimmer-route'; + constructRoutes(this.owner, [testedRoute]); + + const newHighlights = await highlightsPromise(testedRoute, true); + + matchHighlights(assert, testedRoute, newHighlights, true); + }); + + test('Should highlight tagName div Ember component', async function (assert) { + assert.expect(isComponentHighlightSupported ? 6 : 3); + + const testedRoute = 'div-tag-route'; + constructRoutes(this.owner, [testedRoute]); + + const newHighlights = await highlightsPromise(testedRoute); + + matchHighlights(assert, testedRoute, newHighlights); + }); + + test('Should highlight two rootNode ([rootNode, rootNode] and tagName div) Ember component', async function (assert) { + assert.expect(isComponentHighlightSupported ? 6 : 3); + + const testedRoute = 'div-roots-route'; + constructRoutes(this.owner, [testedRoute]); + + const newHighlights = await highlightsPromise(testedRoute); + + matchHighlights(assert, testedRoute, newHighlights); + }); });