Files
ionic-framework/core/src/utils/test/utils.ts
Liam DeBeasi 30ca46ab12 feat(animation): add animation utility (#18918)
* Add new keyframes proof of concept

* update esm import

* add base before and after methods, add tests

* add base before and after hooks

* update clean up methods, add tests

* add web animations support, change to arrow functions

* remove console logs

* add from, to, fromTo, and other properties

* add more tests, fix onFinish functionality, being testing with nav transitions

* add progress methods, use force linear

* run linter

* Add playSync

* integrate animations with framework components

* onFinish now supports multiple callbacks

* change const to let

* testing reverse

* add support for both animation utilities

* bug fix

* export createAnimation, a few tweaks

* add base tests

* fix issue with onFinish being called out of order. added tests

* fix race conditions in tests

* clean up

* fix bug where onFinish not calling for empty elements array,  update test

* clean up

* fix treeshaking, remove old comments

* remove old tests

* Add test for animationbuilder backwards compat

* update typings for menu controller

* mock web animations in tests

* run build

* fix type errors

* sync with master

* use requestAnimationFrame instead of writeTask

* fix flaky tests, fix menu

* fix ordering

* update webdriver

* fix wrong version

* Revert "fix wrong version"

This reverts commit be91296e97.

Revert chromedriver update

* Revert "update webdriver"

This reverts commit e49bc9d76e.

Revert chromedriver update

* expose raw animation object, add tests

* add stylesheet recycling

* finalize before and after hook tests

* a few styling changes

* fix lint warnings

* get rid of old code

* Fix progressStep overflow bug

* disable reuse stylesheet

* small updates

* fix old animation create

* setStyleProperty helper

* reuse keyframe styles

* keyframes

* fix css animation issue with display: none, add tests

* add comment

* fix issue with progress animations and css animations

* clean up

* clean up pt2

* fix tests

* fix linter

* add fill for overlays

* fix swipe to go back

* clean up css animations when done

* fix edge cases with css animations

* fix menu open and close

* add reset function

* clean up reset fn

* Fix issue where animation always being reset

* allow updating animations on the fly

* add clear onfinish method

* fix linter

* add callback options, expand force direction

* ensure opts is defined

* fix css animations open and close for menus

* remove test

* add extra check

* clean up

* fix css anim bug swipe to go back

* fix pause

* setup alt animation to avoid flickering

* clean up

* reset flags on destroy

* add ability to change duration on progressEnd

* fix flicker on duration change for css animations

* fix ios transition

* remove unneeded recursion

* increase durability of updating css animations on the fly

* fix gesture anim

* fix web anim as well. more work for cleanup

* simplify progressEnd for css animations

* fix swipe to go back race condition

* clean up

* Add todo

* fix one more bug
2019-08-12 10:05:04 -04:00

150 lines
5.0 KiB
TypeScript

import { E2EElement, E2EPage } from '@stencil/core/testing';
import { ElementHandle } from 'puppeteer';
export const generateE2EUrl = (component: string, type: string, rtl = false): string => {
let url = `/src/components/${component}/test/${type}?ionic:_testing=true`;
if (rtl) {
url = `${url}&rtl=true`;
}
return url;
};
/**
* Gets the value of a property on an element
*/
export const getElementProperty = async (element: any, property: string): Promise<string> => {
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
* @param eventType: string - The event name to listen for. ex: `ionPickerColChange`
* @param element: HTMLElement - An HTML element
* @param callbackName: string - The name of the callback function to
* call when the event is fired.
*
* Note: The callback function must be added using
* page.exposeFunction prior to calling this function.
*/
export const listenForEvent = async (page: any, eventType: string, element: any, callbackName: string): Promise<any> => {
try {
return await page.evaluate((scopeEventType: string, scopeElement: any, scopeCallbackName: string) => {
scopeElement.addEventListener(scopeEventType, (e: any) => {
(window as any)[scopeCallbackName]({ detail: e.detail });
});
}, eventType, element, callbackName);
} catch (err) {
throw err;
}
};
/**
* Drags an element by (x, y) pixels
* @param element: HTMLElement - The HTML Element to drag
* @param page - The Puppeteer 'page' object
* @param x: number - Amount to drag `element` by on the x-axis
* @param y: number - Amount to drag `element` by on the y-axis
*/
export const dragElementBy = async (element: any, page: any, x = 0, y = 0): Promise<void> => {
try {
const boundingBox = await element.boundingBox();
const startX = boundingBox.x + boundingBox.width / 2;
const startY = boundingBox.y + boundingBox.height / 2;
const endX = startX + x;
const endY = startY + y;
const midX = endX / 2;
const midY = endY / 2;
await page.mouse.move(startX, startY);
await page.mouse.down();
await page.mouse.move(midX, midY);
await page.mouse.move(endX, endY);
await page.mouse.up();
} catch (err) {
throw err;
}
};
/**
* Wait for a function to return true
* This method runs in the context of the
* test whereas page.waitForFunction runs in
* the context of the browser
* @param fn - The function to run
* @param params: any - Any parameters that the fn needs
* @param interval: number - Interval to run setInterval on
*/
export const waitForFunctionTestContext = async (fn: any, params: any, interval = 16): Promise<any> => {
return new Promise(resolve => {
const intervalId = setInterval(() => {
if (fn(params)) {
clearInterval(intervalId);
return resolve();
}
}, interval);
});
};
/**
* Pierce through shadow roots
* https://github.com/GoogleChrome/puppeteer/issues/858#issuecomment-359763824
*/
export const queryDeep = async (page: E2EPage, ...selectors: string[]): Promise<ElementHandle> => {
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); }
});
};
/**
* Given an element and optional selector, use the selector if
* it exists or get the node name of that element if not. Combine
* with the current mode to verify the correct classes exist.
*
* @param el: E2EElement - The element to verify classes on
* @param selector: string - A selector to use instead of the element tag name
*
* Examples:
* await checkComponentModeClasses(await page.find('ion-card-content'))
* => expect(el).toHaveClass(`card-content-{mode}`);
*
* await checkComponentModeClasses(await page.find('ion-card-content'), 'some-class')
* => expect(el).toHaveClass(`some-class-{mode}`);
*/
export const checkComponentModeClasses = async (el: E2EElement, selector?: string) => {
// If passed a selector to use, use that, else grab the nodeName
// of the element and remove the ion prefix to get the class selector
const component = selector !== undefined ? selector : el.nodeName.toLowerCase().replace('ion-', '');
const mode = await el.getProperty('mode');
expect(el).toHaveClass(`${component}-${mode}`);
};
/**
* Given an element, get the mode and verify it exists as a class
*
* @param el: E2EElement - the element to verify the mode class on
*/
export const checkModeClasses = async (el: E2EElement, globalMode: string) => {
const mode = (await el.getProperty('mode')) || globalMode;
expect(el).toHaveClass(`${mode}`);
};