-
Notifications
You must be signed in to change notification settings - Fork 6.8k
virtual-scroll: add e2e tests for autosize scroll strategy #11345
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,117 @@ | ||
import {browser, by, element, ElementFinder} from 'protractor'; | ||
import {ILocation, ISize} from 'selenium-webdriver'; | ||
|
||
declare var window: any; | ||
|
||
|
||
describe('autosize cdk-virtual-scroll', () => { | ||
let viewport: ElementFinder; | ||
|
||
describe('with uniform items', () => { | ||
beforeEach(() => { | ||
browser.get('/virtual-scroll'); | ||
viewport = element(by.css('.demo-virtual-scroll-uniform-size cdk-virtual-scroll-viewport')); | ||
}); | ||
|
||
it('should scroll down slowly', async () => { | ||
await browser.executeAsyncScript(smoothScrollViewportTo, viewport, 2000); | ||
const offScreen = element(by.css('.demo-virtual-scroll-uniform-size [data-index="39"]')); | ||
const onScreen = element(by.css('.demo-virtual-scroll-uniform-size [data-index="40"]')); | ||
expect(await isVisibleInViewport(offScreen, viewport)).toBe(false); | ||
expect(await isVisibleInViewport(onScreen, viewport)).toBe(true); | ||
}); | ||
|
||
it('should jump scroll position down and slowly scroll back up', async () => { | ||
// The estimate of the total content size is exactly correct, so we wind up scrolled to the | ||
// same place as if we slowly scrolled down. | ||
await browser.executeAsyncScript(scrollViewportTo, viewport, 2000); | ||
const offScreen = element(by.css('.demo-virtual-scroll-uniform-size [data-index="39"]')); | ||
const onScreen = element(by.css('.demo-virtual-scroll-uniform-size [data-index="40"]')); | ||
expect(await isVisibleInViewport(offScreen, viewport)).toBe(false); | ||
expect(await isVisibleInViewport(onScreen, viewport)).toBe(true); | ||
|
||
// As we slowly scroll back up we should wind up back at the start of the content. | ||
await browser.executeAsyncScript(smoothScrollViewportTo, viewport, 0); | ||
const first = element(by.css('.demo-virtual-scroll-uniform-size [data-index="0"]')); | ||
expect(await isVisibleInViewport(first, viewport)).toBe(true); | ||
}); | ||
}); | ||
|
||
describe('with variable size', () => { | ||
beforeEach(() => { | ||
browser.get('/virtual-scroll'); | ||
viewport = element(by.css('.demo-virtual-scroll-variable-size cdk-virtual-scroll-viewport')); | ||
}); | ||
|
||
it('should scroll down slowly', async () => { | ||
await browser.executeAsyncScript(smoothScrollViewportTo, viewport, 2000); | ||
const offScreen = element(by.css('.demo-virtual-scroll-variable-size [data-index="19"]')); | ||
const onScreen = element(by.css('.demo-virtual-scroll-variable-size [data-index="20"]')); | ||
expect(await isVisibleInViewport(offScreen, viewport)).toBe(false); | ||
expect(await isVisibleInViewport(onScreen, viewport)).toBe(true); | ||
}); | ||
|
||
it('should jump scroll position down and slowly scroll back up', async () => { | ||
// The estimate of the total content size is slightly different than the actual, so we don't | ||
// wind up in the same spot as if we scrolled slowly down. | ||
await browser.executeAsyncScript(scrollViewportTo, viewport, 2000); | ||
const offScreen = element(by.css('.demo-virtual-scroll-variable-size [data-index="18"]')); | ||
const onScreen = element(by.css('.demo-virtual-scroll-variable-size [data-index="19"]')); | ||
expect(await isVisibleInViewport(offScreen, viewport)).toBe(false); | ||
expect(await isVisibleInViewport(onScreen, viewport)).toBe(true); | ||
|
||
// As we slowly scroll back up we should wind up back at the start of the content. As we | ||
// scroll the error from when we jumped the scroll position should be slowly corrected. | ||
await browser.executeAsyncScript(smoothScrollViewportTo, viewport, 0); | ||
const first = element(by.css('.demo-virtual-scroll-variable-size [data-index="0"]')); | ||
expect(await isVisibleInViewport(first, viewport)).toBe(true); | ||
}); | ||
}); | ||
}); | ||
|
||
|
||
/** Checks if the given element is visible in the given viewport. */ | ||
async function isVisibleInViewport(el: ElementFinder, viewport: ElementFinder): Promise<boolean> { | ||
if (!await el.isPresent() || !await el.isDisplayed() || !await viewport.isPresent() || | ||
!await viewport.isDisplayed()) { | ||
return false; | ||
} | ||
const viewportRect = getRect(await viewport.getLocation(), await viewport.getSize()); | ||
const elRect = getRect(await el.getLocation(), await el.getSize()); | ||
return elRect.left < viewportRect.right && elRect.right > viewportRect.left && | ||
elRect.top < viewportRect.bottom && elRect.bottom > viewportRect.top; | ||
} | ||
|
||
|
||
/** Gets the rect for an element given its location ans size. */ | ||
function getRect(location: ILocation, size: ISize): | ||
{top: number, left: number, bottom: number, right: number} { | ||
return { | ||
top: location.y, | ||
left: location.x, | ||
bottom: location.y + size.height, | ||
right: location.x + size.width | ||
}; | ||
} | ||
|
||
|
||
/** Immediately scrolls the viewport to the given offset. */ | ||
function scrollViewportTo(viewportEl: any, offset: number, done: () => void) { | ||
viewportEl.scrollTop = offset; | ||
window.requestAnimationFrame(() => done()); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not sure whether this wouldn't introduce flakes in the CI. Most browsers will pause or throttle the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Interesting, I need to pass some time since the scroll events are sampled every animation frame. If this turns out to be flaky I can switch to |
||
} | ||
|
||
|
||
/** Smoothly scrolls the viewport to the given offset, 25px at a time. */ | ||
function smoothScrollViewportTo(viewportEl: any, offset: number, done: () => void) { | ||
let promise = Promise.resolve(); | ||
let curOffset = viewportEl.scrollTop; | ||
do { | ||
const co = curOffset += Math.min(25, Math.max(-25, offset - curOffset)); | ||
promise = promise.then(() => new Promise<void>(resolve => { | ||
viewportEl.scrollTop = co; | ||
window.requestAnimationFrame(() => resolve()); | ||
})); | ||
} while (curOffset != offset); | ||
promise.then(() => done()); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
.demo-viewport { | ||
height: 300px; | ||
width: 300px; | ||
box-shadow: 0 0 0 1px black; | ||
} | ||
|
||
.demo-item { | ||
background: magenta; | ||
} | ||
|
||
.demo-odd { | ||
background: cyan; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
<section class="demo-virtual-scroll-uniform-size"> | ||
<h3>Uniform size</h3> | ||
<cdk-virtual-scroll-viewport class="demo-viewport" autosize> | ||
<div *cdkVirtualFor="let size of uniformItems; let i = index; let odd = odd" class="demo-item" | ||
[style.height.px]="size" [class.demo-odd]="odd" [attr.data-index]="i"> | ||
Uniform Item #{{i}} - ({{size}}px) | ||
</div> | ||
</cdk-virtual-scroll-viewport> | ||
</section> | ||
|
||
<section class="demo-virtual-scroll-variable-size"> | ||
<h3>Random size</h3> | ||
<cdk-virtual-scroll-viewport class="demo-viewport" autosize> | ||
<div *cdkVirtualFor="let size of variableItems; let i = index; let odd = odd" class="demo-item" | ||
[style.height.px]="size" [class.demo-odd]="odd" [attr.data-index]="i"> | ||
Variable Item #{{i}} - ({{size}}px) | ||
</div> | ||
</cdk-virtual-scroll-viewport> | ||
</section> |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
import {Component} from '@angular/core'; | ||
|
||
|
||
const itemSizeSample = [100, 25, 50, 50, 100, 200, 75, 100, 50, 250]; | ||
|
||
|
||
@Component({ | ||
moduleId: module.id, | ||
selector: 'virtual-scroll-e2e', | ||
templateUrl: 'virtual-scroll-e2e.html', | ||
styleUrls: ['virtual-scroll-e2e.css'], | ||
}) | ||
export class VirtualScrollE2E { | ||
uniformItems = Array(1000).fill(50); | ||
variableItems = Array(100).fill(0).reduce(acc => acc.concat(itemSizeSample), []); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: consider naming this something along the lines of
isCompletelyInViewport
?isVisibileInViewport
could apply to something that's partially in the viewport.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It does apply to things that are partially in the viewport, that is intentional.