From 352797e93295a228e3033bcb370e253b641efac8 Mon Sep 17 00:00:00 2001 From: Adam LaCombe Date: Tue, 26 Mar 2019 17:00:44 -0400 Subject: [PATCH] fix(reorder-group): add ability to reorder items inside shadow (#17747) - adds ability to reorder items from within a shadow dom component - adds interactive test fixes #17746 --- .../reorder-group/reorder-group.tsx | 2 +- .../reorder-group/test/interactive/e2e.ts | 60 +++++++++++++++++++ .../reorder-group/test/interactive/index.html | 55 +++++++++++++++++ .../reorder-group/test/test.utils.ts | 22 +++++++ core/src/utils/test/utils.ts | 32 ++++++++++ 5 files changed, 170 insertions(+), 1 deletion(-) create mode 100644 core/src/components/reorder-group/test/interactive/e2e.ts create mode 100644 core/src/components/reorder-group/test/interactive/index.html create mode 100644 core/src/components/reorder-group/test/test.utils.ts diff --git a/core/src/components/reorder-group/reorder-group.tsx b/core/src/components/reorder-group/reorder-group.tsx index cc91fb10ca..ac25871712 100644 --- a/core/src/components/reorder-group/reorder-group.tsx +++ b/core/src/components/reorder-group/reorder-group.tsx @@ -64,7 +64,7 @@ export class ReorderGroup implements ComponentInterface { } this.gesture = (await import('../../utils/gesture')).createGesture({ - el: this.doc.body, + el: this.el, queue: this.queue, gestureName: 'reorder', gesturePriority: 110, diff --git a/core/src/components/reorder-group/test/interactive/e2e.ts b/core/src/components/reorder-group/test/interactive/e2e.ts new file mode 100644 index 0000000000..dc437e62a6 --- /dev/null +++ b/core/src/components/reorder-group/test/interactive/e2e.ts @@ -0,0 +1,60 @@ +import * as pd from '@stencil/core/dist/testing/puppeteer/puppeteer-declarations'; +import { newE2EPage } from '@stencil/core/testing'; + +import { getElementProperty, queryDeep } from '../../../../utils/test/utils'; +import { moveReorderItem } from '../test.utils'; + +test('reorder: interactive', async () => { + const page = await newE2EPage({ + url: '/src/components/reorder-group/test/interactive?ionic:_testing=true' + }); + + const compares = []; + compares.push(await page.compareScreenshot('reorder: interactive before move')); + + const items = await page.$$('ion-reorder'); + const getItemId = await getElementProperty(items[0], 'id'); + expect(getItemId).toEqual('item-0'); + + await moveItem(getItemId, page, 'down', 1); + + const itemsAfterFirstMove = await page.$$('ion-reorder'); + expect(await getElementProperty(itemsAfterFirstMove[0], 'id')).toEqual('item-1'); + + await moveItem(getItemId, page, 'up', 1); + + const itemsAfterSecondMove = await page.$$('ion-reorder'); + expect(await getElementProperty(itemsAfterSecondMove[0], 'id')).toEqual('item-0'); + + compares.push(await page.compareScreenshot('reorder: interactive after move; before shadow move')); + + const shadowDomList = await queryDeep(page, 'test-reorder-list-shadow-dom', 'ion-list'); + + const itemsInShadowRoot = await shadowDomList.$$('ion-reorder'); + const getShadowItemId = await getElementProperty(itemsInShadowRoot[0], 'id'); + expect(getShadowItemId).toEqual('item-0'); + + await moveItem(getShadowItemId, page, 'down', 1, 'test-reorder-list-shadow-dom', 'ion-list'); + + const itemsInShadowRootAfterFirstMove = await shadowDomList.$$('ion-reorder'); + expect(await getElementProperty(itemsInShadowRootAfterFirstMove[0], 'id')).toEqual('item-1'); + + await moveItem(getShadowItemId, page, 'up', 1, 'test-reorder-list-shadow-dom', 'ion-list'); + + const itemsInShadowRootAfterSecondMove = await shadowDomList.$$('ion-reorder'); + expect(await getElementProperty(itemsInShadowRootAfterSecondMove[0], 'id')).toEqual('item-0'); + + compares.push(await page.compareScreenshot('reorder: interactive after shadow move')); + + for (const compare of compares) { + expect(compare).toMatchScreenshot(); + } +}); + +async function moveItem(id: string, page: pd.E2EPage, direction: 'up' | 'down' = 'up', numberOfSpaces = 1, ...parentSelectors: string[]) { + try { + await moveReorderItem(`#${id}`, page, direction, numberOfSpaces, ...parentSelectors); + } catch (err) { + throw err; + } +} diff --git a/core/src/components/reorder-group/test/interactive/index.html b/core/src/components/reorder-group/test/interactive/index.html new file mode 100644 index 0000000000..3aaae4e847 --- /dev/null +++ b/core/src/components/reorder-group/test/interactive/index.html @@ -0,0 +1,55 @@ + + + + + + Reorder - Interactive + + + + + + + + + + + + + diff --git a/core/src/components/reorder-group/test/test.utils.ts b/core/src/components/reorder-group/test/test.utils.ts new file mode 100644 index 0000000000..747a8aef1a --- /dev/null +++ b/core/src/components/reorder-group/test/test.utils.ts @@ -0,0 +1,22 @@ +import * as pd from '@stencil/core/dist/testing/puppeteer/puppeteer-declarations'; + +import { dragElementBy, queryDeep } from '../../../utils/test/utils'; + +/** + * Moves a reorder item by simulating a drag event + */ +export async function moveReorderItem(id: string, page: pd.E2EPage, direction: 'up' | 'down' = 'up', numberOfSpaces = 1, ...parentSelectors: string[]) { + try { + const reorderItem = parentSelectors && parentSelectors.length > 0 ? await (await queryDeep(page, ...parentSelectors)).$(id) : await page.$(id); + + if (!reorderItem) { throw new Error('Reorder Item is undefined'); } + + const boundingBox = await reorderItem.boundingBox(); + if (!boundingBox) { throw new Error('Reorder Item bounding box is undefined'); } + + await dragElementBy(reorderItem, page, 0, (direction === 'up') ? -(boundingBox.height * numberOfSpaces) : (boundingBox.height * numberOfSpaces)); + + } catch (err) { + throw err; + } +} diff --git a/core/src/utils/test/utils.ts b/core/src/utils/test/utils.ts index cca317df34..d3656c219c 100644 --- a/core/src/utils/test/utils.ts +++ b/core/src/utils/test/utils.ts @@ -1,3 +1,6 @@ +import { E2EPage } from '@stencil/core/testing'; +import { ElementHandle } from 'puppeteer'; + export function generateE2EUrl(component: string, type: string, rtl = false): string { let url = `/src/components/${component}/test/${type}?ionic:_testing=true`; if (rtl) { @@ -14,6 +17,16 @@ export function cleanScreenshotName(screenshotName: string): string { .toLowerCase(); } +/** + * Gets the value of a property on an element + */ +export const getElementProperty = async (element: any, property: string): Promise => { + const getProperty = await element.getProperty(property); + if (!getProperty) { return ''; } + + return getProperty.jsonValue(); +}; + /** * Listens for an event and fires a callback * @param page - The Puppeteer `page` object @@ -87,3 +100,22 @@ export const waitForFunctionTestContext = async (fn: any, params: any, interval }, interval); }); }; + +/** + * Pierce through shadow roots + * https://github.com/GoogleChrome/puppeteer/issues/858#issuecomment-359763824 + */ +export async function queryDeep(page: E2EPage, ...selectors: string[]): Promise { + const shadowSelectorFn = (el: Element, selector: string): Element | null => (el && el.shadowRoot) && el.shadowRoot.querySelector(selector); + + return new Promise(async resolve => { + const [ firstSelector, ...restSelectors ] = selectors; + let parentElement = await page.$(firstSelector); + + for (const selector of restSelectors) { + parentElement = await page.evaluateHandle(shadowSelectorFn, parentElement, selector) as any; + } + + if (parentElement) { resolve(parentElement); } + }); +}