diff --git a/addon-test-support/pages/-private/ember-table-header.js b/addon-test-support/pages/-private/ember-table-header.js index 11be5df12..6c91ca5cf 100644 --- a/addon-test-support/pages/-private/ember-table-header.js +++ b/addon-test-support/pages/-private/ember-table-header.js @@ -5,6 +5,17 @@ import { click } from '@ember/test-helpers'; import { mouseDown, mouseMove, mouseUp } from '../../helpers/mouse'; import { getScale } from '../../helpers/element'; +function computedStyleInPixels(target, property) { + let stringValue = window.getComputedStyle(target)[property]; + let numberValue = Number(stringValue.substring(0, stringValue.length - 2)); + if (isNaN(numberValue)) { + throw new Error( + `computedStyleInPixels failed to convert the computed style property of '${property}' into a Number. Value was '${stringValue}'` + ); + } + return numberValue; +} + export const SortPage = PageObject.extend({ indicator: { scope: '[data-test-sort-indicator]', @@ -29,6 +40,11 @@ const Header = PageObject.extend({ /** * Retrieves selected header cell width. + * + * offsetWidth returns a rounded integer, and so can + * result in unreliable tests. + * + * @returns {number} */ get width() { return findElement(this).offsetWidth; @@ -36,11 +52,34 @@ const Header = PageObject.extend({ /** * Retrieves selected header cell height. + * + * offsetHeight returns a rounded integer, and so can + * result in unreliable tests. + * + * @returns {number} */ get height() { return findElement(this).offsetHeight; }, + /** + * Retrieves selected header cell logical width. + * + * @returns {number} + */ + get logicalWidth() { + return computedStyleInPixels(findElement(this), 'width'); + }, + + /** + * Retrieves the rendered width of the selected header cell. + * + * @returns {number} + */ + get renderedWidth() { + return findElement(this).getBoundingClientRect().width; + }, + get isLeaf() { return findElement(this).dataset.testLeafHeader; }, @@ -59,10 +98,26 @@ const Header = PageObject.extend({ contextMenu: triggerable('contextmenu'), /** - * Resizes this column by dragging right border several pixels. + * Resize the table header. This API isn't clear about if a logical + * or rendered size is being passed. It defers to the more explicit + * logicalResize which should probably be preferred in future use. */ async resize(targetSize) { - let resizeHandle = findElement(this, '.et-header-resize-area'); + await this.logicalResize(targetSize); + }, + + async logicalResize(targetSize) { + let renderedTargetSize = + targetSize / getScale(document.getElementById('ember-testing-container').firstElementChild); + await this.renderedResize(renderedTargetSize); + }, + + /** + * Resizes this column by dragging right border several pixels, + * unless the column is fixed right in quick case it drags left. + */ + async renderedResize(targetSize) { + let resizeHandle = findElement(this, '[data-test-resize-handle]'); if (!resizeHandle) { return; @@ -70,16 +125,31 @@ const Header = PageObject.extend({ let box = resizeHandle.getBoundingClientRect(); let startX = (box.right + box.left) / 2; - let deltaX = (targetSize - this.width) / getScale(resizeHandle); + let y = box.top + (box.bottom - box.top) / 2; + let deltaX = targetSize - this.renderedWidth; if (this.isFixedRight) { deltaX = -deltaX; } - await mouseDown(resizeHandle, startX, resizeHandle.clientHeight / 2); - await mouseMove(resizeHandle, startX + 20, resizeHandle.clientHeight / 2); - await mouseMove(resizeHandle, startX + deltaX, resizeHandle.clientHeight / 2); - await mouseUp(resizeHandle, startX + deltaX, resizeHandle.clientHeight / 2); + let finalX = startX + deltaX; + + await mouseDown(resizeHandle, startX, y); + + /** + * Below a certain number of steps, Hammer (the gesture library + * which recognizes panning) will not pick up the pointermove + * events emitted by `mouseMove` before the gestrure completes. + * + * 5 seems a reasonable number. + */ + let current = startX; + for (let steps = 5; steps > 0; steps--) { + await mouseMove(resizeHandle, current, y); + current = current + (finalX - current) / steps; + } + await mouseMove(resizeHandle, finalX, y); + await mouseUp(resizeHandle, finalX, y); }, /** diff --git a/addon-test-support/pages/ember-table.js b/addon-test-support/pages/ember-table.js index 6c73deebf..d90898f1a 100644 --- a/addon-test-support/pages/ember-table.js +++ b/addon-test-support/pages/ember-table.js @@ -50,6 +50,11 @@ export default PageObject.extend({ /** * Returns the table width. + * + * offsetWidth returns a rounded integer, and so can + * result in unreliable tests. + * + * @returns {number} */ get width() { return findElement(this, 'table').offsetWidth; @@ -57,6 +62,11 @@ export default PageObject.extend({ /** * Returns the table container width. + * + * offsetWidth returns a rounded integer, and so can + * result in unreliable tests. + * + * @returns {number} */ get containerWidth() { return findElement(this).offsetWidth; diff --git a/tests/integration/components/cell-test.js b/tests/integration/components/cell-test.js index 28daca59b..400aa9f9c 100644 --- a/tests/integration/components/cell-test.js +++ b/tests/integration/components/cell-test.js @@ -193,8 +193,16 @@ module('Integration | cell', function() { assert.ok(cell.isLastColumn, 'is-last-column applied to normal cell'); assert.notOk(slackCell.isLastColumn, 'is-last-column not applied to slack cell'); - // shrink cell "A"; now slack column gets the `is-last-column` class - await header.resize(header.width - 1); + /** + * shrink cell "A"; now slack column gets the `is-last-column` + * class. + * + * The number 27 here for the resize is fairly arbitrary, it may be + * tied to the size of the resize handle or another drag/drop + * threshold. + */ + await header.logicalResize(header.logicalWidth - 27); + assert.notOk(cell.isLastColumn, 'is-last-column not applied to normal cell'); assert.ok(slackCell.isLastColumn, 'is-last-column applied to slack cell'); }); diff --git a/tests/integration/components/headers/ember-th-test.js b/tests/integration/components/headers/ember-th-test.js index 32bfa91ce..1a9d015b7 100644 --- a/tests/integration/components/headers/ember-th-test.js +++ b/tests/integration/components/headers/ember-th-test.js @@ -137,8 +137,16 @@ module('[Unit] ember-th', function(hooks) { assert.ok(header.isLastColumn, 'is-last-column applied to normal header'); assert.notOk(slackHeader.isLastColumn, 'is-last-column not applied to slack header'); - // shrink header "A"; now slack column gets the `is-last-column` class - await header.resize(header.width - 1); + /** + * shrink header "A"; now slack column gets the `is-last-column` + * class. + * + * The number 27 here for the resize is fairly arbitrary, it may be + * tied to the size of the resize handle or another drag/drop + * threshold. + */ + await header.logicalResize(header.logicalWidth - 27); + assert.notOk(header.isLastColumn, 'is-last-column not applied to normal header'); assert.ok(slackHeader.isLastColumn, 'is-last-column applied to slack header'); }); diff --git a/tests/integration/components/headers/reorder-test.js b/tests/integration/components/headers/reorder-test.js index 4af9f2ea0..42c057316 100644 --- a/tests/integration/components/headers/reorder-test.js +++ b/tests/integration/components/headers/reorder-test.js @@ -11,34 +11,56 @@ import { parameterizedComponentModule } from '../../../helpers/module'; import { find, findAll } from '@ember/test-helpers'; import scrollTo from '../../../helpers/scroll-to'; import { mouseDown, mouseMove, mouseUp } from 'ember-table/test-support/helpers/mouse'; -import { getScale } from 'ember-table/test-support/helpers/element'; import TablePage from 'ember-table/test-support/pages/ember-table'; import { toBase26 } from 'dummy/utils/base-26'; const table = new TablePage(); +/** + * + * Scroll to a given edge, accepting an offset for how many rendered + * pixels to stay from the edge. + * + */ export async function scrollToEdge(targetElement, edgeOffset, direction) { let targetElementRight = targetElement.getBoundingClientRect().right; let container = find('.ember-table'); - let scale = getScale(container); - let edge; + let initialTargetX, finalTargetX; if (direction === 'right') { - await mouseDown(targetElement, targetElementRight - 30, 0); - await mouseMove(targetElement, targetElementRight - 20, 0); - await mouseMove(targetElement, targetElementRight - 10, 0); - - edge = container.getBoundingClientRect().right - edgeOffset / scale; + initialTargetX = targetElementRight - 5; + finalTargetX = container.getBoundingClientRect().right - edgeOffset; + if (initialTargetX >= finalTargetX) { + throw new Error( + 'When dragging right, the starting position X must be smaller than the ending position' + ); + } } else { - await mouseDown(targetElement, targetElementRight - 10, 0); - await mouseMove(targetElement, targetElementRight - 20, 0); - await mouseMove(targetElement, targetElementRight - 30, 0); - - edge = container.getBoundingClientRect().left + edgeOffset / scale; + initialTargetX = targetElementRight - 5; + finalTargetX = container.getBoundingClientRect().left + edgeOffset; + if (initialTargetX <= finalTargetX) { + throw new Error( + 'When dragging left, the starting position X must be greater than the ending position' + ); + } } - await mouseMove(targetElement, edge, 0); + await mouseDown(targetElement, initialTargetX, 0); + + /* + * Below a certain number of steps, Hammer (the gesture library + * which recognizes panning) will not pick up the pointermove + * events emitted by `mouseMove` before the gestrure completes. + * + * 5 seems a reasonable number. + */ + let current = initialTargetX; + for (let steps = 5; steps > 0; steps--) { + await mouseMove(targetElement, current, 0); + current = current + (finalTargetX - current) / steps; + } + await mouseMove(targetElement, finalTargetX, 0); await mouseUp(targetElement); } @@ -309,7 +331,9 @@ module('Integration | headers | reorder', function() { let tableOverflowContainer = find('[data-test-ember-table-overflow]'); let header = findAll('th')[0]; - await reorderToRightEdge(header, columnWidth); + let headerBoundingRect = header.getBoundingClientRect(); + let headerWidth = headerBoundingRect.right - headerBoundingRect.left; + await reorderToRightEdge(header, headerWidth); assert.ok(tableOverflowContainer.scrollLeft > 0, 'table scrolled'); assert.equal(table.headers.objectAt(columnCount - 2).text, toBase26(0), 'table scrolled'); @@ -331,7 +355,9 @@ module('Integration | headers | reorder', function() { let header = findAll('th')[columnCount - 1]; await scrollTo(tableOverflowContainer, 10000, 0); - await reorderToLeftEdge(header, columnWidth); + let headerBoundingRect = header.getBoundingClientRect(); + let headerWidth = headerBoundingRect.right - headerBoundingRect.left; + await reorderToLeftEdge(header, headerWidth); assert.equal(tableOverflowContainer.scrollLeft, 0, 'table scrolled back to the left'); assert.equal(table.headers.objectAt(1).text, toBase26(columnCount - 1), 'table scrolled'); diff --git a/tests/integration/components/headers/resize-test.js b/tests/integration/components/headers/resize-test.js index 84b24967a..7cf03aaca 100644 --- a/tests/integration/components/headers/resize-test.js +++ b/tests/integration/components/headers/resize-test.js @@ -69,14 +69,17 @@ module('Integration | header | resize', function() { }); test('column resize action is sent up to controller', async function(assert) { - this.set('onResize', function(column) { - assert.equal(column.name, 'B', 'action is sent to controller after resizing'); + let calls = []; + this.set('onResize', (...args) => { + calls.push(args); }); await generateTable(this, { widthConstraint: 'eq-container' }); let originalWidth = table.headers.objectAt(1).width; - await table.headers.objectAt(1).resize(originalWidth + 20); + await table.headers.objectAt(1).resize(originalWidth + 30); + assert.equal(calls.length, 1, 'resize called once'); + assert.equal(calls[0][0].name, 'B', 'The correct resized column ("B") is passed'); }); test('can disable resize per column', async function(assert) { diff --git a/tests/unit/-private/table-sticky-polyfill-test.js b/tests/unit/-private/table-sticky-polyfill-test.js index 3ba73dd41..2a93dc6d2 100644 --- a/tests/unit/-private/table-sticky-polyfill-test.js +++ b/tests/unit/-private/table-sticky-polyfill-test.js @@ -134,7 +134,11 @@ function verifyMultiLineHeader(assert) { firstTableCellOfEachHeaderRow.forEach(cell => { let firstCellRect = cell.getBoundingClientRect(); expectedOffset += firstCellRect.height; - assert.equal(expectedOffset, firstCellRect.bottom); + assert.equal( + expectedOffset, + firstCellRect.bottom, + 'bottom of the cell matches based on expected offset' + ); }); }