1// Copyright 2020 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5const {assert} = chai; 6 7import {renderElementIntoDOM, assertElements, assertElement} from './DOMHelpers.js'; 8import {_normalizePositionData, drawGridAreaNames, drawGridLineNumbers, drawGridLineNames, CanvasSize, GridPositionNormalizedDataWithNames, NormalizePositionDataConfig} from '../../../../inspector_overlay/css_grid_label_helpers.js'; 9import {AreaBounds, Bounds} from '../../../../inspector_overlay/common.js'; 10import {gridStyle} from '../../../../inspector_overlay/highlight_grid_common.js'; 11 12const GRID_LABEL_CONTAINER_ID = 'grid-label-container'; 13const DEFAULT_GRID_LABEL_LAYER_ID = 'grid-labels'; 14const GRID_LINE_NUMBER_LABEL_CONTAINER_CLASS = 'line-numbers'; 15const GRID_LINE_NAME_LABEL_CONTAINER_CLASS = 'line-names'; 16const GRID_LINE_AREA_LABEL_CONTAINER_CLASS = 'area-names'; 17const GRID_TRACK_SIZES_LABEL_CONTAINER_CLASS = 'track-sizes'; 18 19/** 20 * This does the same as initFrame but also prepares the DOM for testing grid labels. 21 */ 22export function initFrameForGridLabels() { 23 const styleTag = document.createElement('style'); 24 styleTag.textContent = gridStyle; 25 document.head.append(styleTag); 26 createGridLabelContainer(); 27} 28 29export function initFrameForMultipleGridLabels(numGrids: number) { 30 for (let i = 1; i <= numGrids; i++) { 31 createGridLabelContainer(i); 32 } 33} 34 35export function createGridLabelContainer(layerId?: number) { 36 // Ensure main layer is created first 37 let el = document.getElementById(GRID_LABEL_CONTAINER_ID); 38 if (!el) { 39 el = document.createElement('div'); 40 el.id = GRID_LABEL_CONTAINER_ID; 41 } 42 43 const layerEl = el.createChild('div'); 44 layerEl.id = layerId ? `grid-${layerId}-labels` : DEFAULT_GRID_LABEL_LAYER_ID; 45 layerEl.createChild('div', GRID_LINE_NUMBER_LABEL_CONTAINER_CLASS); 46 layerEl.createChild('div', GRID_LINE_NAME_LABEL_CONTAINER_CLASS); 47 layerEl.createChild('div', GRID_LINE_AREA_LABEL_CONTAINER_CLASS); 48 layerEl.createChild('div', GRID_TRACK_SIZES_LABEL_CONTAINER_CLASS); 49 50 renderElementIntoDOM(el, {allowMultipleChildren: true}); 51} 52 53export function getMainGridLabelContainer(): HTMLElement { 54 const el = document.getElementById(GRID_LABEL_CONTAINER_ID); 55 assertElement(el, HTMLElement); 56 return el; 57} 58 59export function getGridLabelContainer(layerId?: number): HTMLElement { 60 const id = layerId ? `grid-${layerId}-labels` : DEFAULT_GRID_LABEL_LAYER_ID; 61 const el = document.querySelector(`#${GRID_LABEL_CONTAINER_ID} #${CSS.escape(id)}`); 62 assertElement(el, HTMLElement); 63 return el; 64} 65 66export function getGridLineNumberLabelContainer(layerId?: number): HTMLElement { 67 const id = layerId ? `grid-${layerId}-labels` : DEFAULT_GRID_LABEL_LAYER_ID; 68 const el = document.querySelector( 69 `#${GRID_LABEL_CONTAINER_ID} #${CSS.escape(id)} .${GRID_LINE_NUMBER_LABEL_CONTAINER_CLASS}`); 70 assertElement(el, HTMLElement); 71 return el; 72} 73 74export function getGridLineNameLabelContainer(layerId?: number): HTMLElement { 75 const id = layerId ? `grid-${layerId}-labels` : DEFAULT_GRID_LABEL_LAYER_ID; 76 const el = 77 document.querySelector(`#${GRID_LABEL_CONTAINER_ID} #${CSS.escape(id)} .${GRID_LINE_NAME_LABEL_CONTAINER_CLASS}`); 78 assertElement(el, HTMLElement); 79 return el; 80} 81 82export function getGridTrackSizesLabelContainer(layerId?: number): HTMLElement { 83 const id = layerId ? `grid-${layerId}-labels` : DEFAULT_GRID_LABEL_LAYER_ID; 84 const el = document.querySelector( 85 `#${GRID_LABEL_CONTAINER_ID} #${CSS.escape(id)} .${GRID_TRACK_SIZES_LABEL_CONTAINER_CLASS}`); 86 assertElement(el, HTMLElement); 87 return el; 88} 89 90export function getGridAreaNameLabelContainer(layerId?: number): HTMLElement { 91 const id = layerId ? `grid-${layerId}-labels` : DEFAULT_GRID_LABEL_LAYER_ID; 92 const el = 93 document.querySelector(`#${GRID_LABEL_CONTAINER_ID} #${CSS.escape(id)} .${GRID_LINE_AREA_LABEL_CONTAINER_CLASS}`); 94 assertElement(el, HTMLElement); 95 return el; 96} 97 98interface Position { 99 x: number; 100 y: number; 101} 102 103interface TrackSize extends Position { 104 computedSize: number; 105} 106 107interface NamedLinePosition extends Position { 108 name: string; 109} 110 111interface ExpectedLayerLabel { 112 layerId: number; 113 expectedLabels: ExpectedLineNumberLabel[]; 114} 115interface ExpectedLineNumberLabel { 116 className: string; 117 count: number; 118} 119 120interface ExpectedLineNameLabel { 121 type: string; 122 textContent: string; 123 x?: number; 124 y?: number; 125} 126 127interface ExpectedAreaNameLabel { 128 textContent: string; 129 left?: string; 130 top?: string; 131} 132 133export function drawGridLineNumbersAndAssertLabels( 134 config: NormalizePositionDataConfig&{writingMode?: string}, bounds: Bounds, canvasSize: CanvasSize, layerId: number, 135 expectedLabels: ExpectedLineNumberLabel[]) { 136 const el = getGridLineNumberLabelContainer(layerId); 137 const data = _normalizePositionData(config, bounds); 138 139 // Note that this test helper is focused on testing the number and orientation of the labels, not their exact position 140 // so we pass the identity matrix here in all cases, even when a different writing mode is provided. 141 drawGridLineNumbers(el, data, canvasSize, new DOMMatrix(), config.writingMode); 142 let totalLabelCount = 0; 143 for (const {className, count} of expectedLabels) { 144 const labels = 145 el.querySelectorAll(`.${GRID_LINE_NUMBER_LABEL_CONTAINER_CLASS} .grid-label-content.${CSS.escape(className)}`); 146 assert.strictEqual(labels.length, count, `Expected ${count} labels to be displayed for ${className}`); 147 totalLabelCount += count; 148 } 149 150 assert.strictEqual( 151 el.querySelectorAll(`.${GRID_LINE_NUMBER_LABEL_CONTAINER_CLASS} .grid-label-content`).length, totalLabelCount, 152 'The right total number of line number labels were displayed'); 153} 154 155export function drawGridLineNamesAndAssertLabels( 156 config: NormalizePositionDataConfig, bounds: Bounds, canvasSize: CanvasSize, layerId: number, 157 expectedLabels: ExpectedLineNameLabel[]) { 158 const el = getGridLineNameLabelContainer(layerId); 159 const data = _normalizePositionData(config, bounds); 160 drawGridLineNames(el, data as GridPositionNormalizedDataWithNames, canvasSize); 161 162 const labels = el.querySelectorAll(`.${GRID_LINE_NAME_LABEL_CONTAINER_CLASS} .grid-label-content`); 163 assert.strictEqual(labels.length, expectedLabels.length, 'The right total number of line name labels were displayed'); 164 assertElements(labels, HTMLElement); 165 166 const foundLabels: {textContent: string, x: number, y: number}[] = []; 167 labels.forEach(el => { 168 const width = el.offsetWidth; 169 const height = el.offsetHeight; 170 const top = parseInt(el.style.top, 10); 171 const left = parseInt(el.style.left, 10); 172 173 let rowOffset = height / 2; 174 if (el.classList.contains('right-top')) { 175 rowOffset = 0; 176 } else if (el.classList.contains('right-bottom')) { 177 rowOffset = height; 178 } 179 180 let columnOffset = width / 2; 181 if (el.classList.contains('bottom-left')) { 182 columnOffset = 0; 183 } else if (el.classList.contains('bottom-right')) { 184 columnOffset = width; 185 } 186 187 foundLabels.push({ 188 textContent: el.textContent || '', 189 x: left + columnOffset, 190 y: top + rowOffset, 191 }); 192 }); 193 194 for (const expected of expectedLabels) { 195 const foundLabel = foundLabels.find(({textContent}) => textContent === expected.textContent); 196 197 if (!foundLabel) { 198 assert.fail(`Expected line name label with text content ${expected.textContent} not found`); 199 return; 200 } 201 202 if (expected.type === 'column' && typeof expected.x !== 'undefined') { 203 assert.strictEqual( 204 foundLabel.x, expected.x, 205 `Expected column line name label ${expected.textContent} to be positioned at ${expected.x}px`); 206 } 207 if (expected.type === 'row' && typeof expected.y !== 'undefined') { 208 assert.strictEqual( 209 foundLabel.y, expected.y, 210 `Expected row line name label ${expected.textContent} to be positioned at ${expected.y}px`); 211 } 212 } 213} 214 215export function drawGridAreaNamesAndAssertLabels( 216 areaNames: AreaBounds[], writingModeMatrix: DOMMatrix|undefined, writingMode: string|undefined, 217 expectedLabels: ExpectedAreaNameLabel[]) { 218 const el = getGridAreaNameLabelContainer(); 219 drawGridAreaNames(el, areaNames, writingModeMatrix, writingMode); 220 221 const labels = el.querySelectorAll(`.${GRID_LINE_AREA_LABEL_CONTAINER_CLASS} .grid-label-content`); 222 assert.strictEqual(labels.length, expectedLabels.length, 'The right total number of area name labels were displayed'); 223 assertElements(labels, HTMLElement); 224 225 const foundLabels: ExpectedAreaNameLabel[] = []; 226 labels.forEach(label => { 227 foundLabels.push({ 228 textContent: label.textContent || '', 229 top: label.style.top, 230 left: label.style.left, 231 }); 232 }); 233 for (const expected of expectedLabels) { 234 const foundLabel = foundLabels.find(({textContent}) => textContent === expected.textContent); 235 236 if (!foundLabel) { 237 assert.fail(`Expected area label with text content ${expected.textContent} not found`); 238 return; 239 } 240 241 if (typeof expected.left !== 'undefined') { 242 assert.strictEqual( 243 foundLabel.left, expected.left, 244 `Expected label ${expected.textContent} to be left positioned to ${expected.left}`); 245 } 246 if (typeof expected.top !== 'undefined') { 247 assert.strictEqual( 248 foundLabel.top, expected.top, 249 `Expected label ${expected.textContent} to be top positioned to ${expected.top}`); 250 } 251 } 252} 253 254export function drawMultipleGridLineNumbersAndAssertLabels( 255 configs: Array<{config: NormalizePositionDataConfig, layerId: number}>, bounds: Bounds, canvasSize: CanvasSize, 256 expectedLabelList: ExpectedLayerLabel[]) { 257 for (const item of configs) { 258 const el = getGridLineNumberLabelContainer(item.layerId); 259 const data = _normalizePositionData(item.config, bounds); 260 drawGridLineNumbers(el, data, canvasSize); 261 } 262 263 let totalLabelCount = 0; 264 for (const {layerId, expectedLabels} of expectedLabelList) { 265 const el = getGridLineNumberLabelContainer(layerId); 266 for (const {className, count} of expectedLabels) { 267 const labels = el.querySelectorAll( 268 `.${GRID_LINE_NUMBER_LABEL_CONTAINER_CLASS} .grid-label-content.${CSS.escape(className)}`); 269 assert.strictEqual(labels.length, count, `Expected ${count} labels to be displayed for ${className}`); 270 totalLabelCount += count; 271 } 272 } 273 274 const mainLayerEl = getMainGridLabelContainer(); 275 assert.strictEqual( 276 mainLayerEl.querySelectorAll(`.${GRID_LINE_NUMBER_LABEL_CONTAINER_CLASS} .grid-label-content`).length, 277 totalLabelCount, 'The right total number of line number labels were displayed'); 278} 279