feat: testID property for use with e2e testing without interfering with a11y (#9793)

* fix(android): nested frames were sometimes not recreated (#9748)

Co-authored-by: Eduardo Speroni <edusperoni@gmail.com>

* feat: testID property for use with e2e testing without interfering with a11y

* feat: better testID support along a11y

wip

* fix: make sure we have a defined id

* feat: --env.e2e to enable testID

* chore: return if using testID

* chore: cleanup

Co-authored-by: Eduardo Speroni <edusperoni@gmail.com>
Co-authored-by: Igor Randjelovic <rigor789@gmail.com>
This commit is contained in:
Nathan Walker
2022-03-08 14:25:05 -08:00
committed by GitHub
parent 86fdf5810a
commit 8be543bcc7
17 changed files with 123 additions and 37 deletions

View File

@@ -645,6 +645,11 @@ function applyContentDescription(view: Partial<View>, forceUpdate?: boolean) {
const contentDescription = contentDescriptionBuilder.join('. ').trim().replace(/^\.$/, '');
if (typeof __USE_TEST_ID__ !== 'undefined' && __USE_TEST_ID__ && view.testID) {
// ignore when testID is enabled
return;
}
if (contentDescription) {
if (Trace.isEnabled()) {
Trace.write(`${cls} - set to "${contentDescription}"`, Trace.categories.Accessibility);

View File

@@ -133,6 +133,7 @@ declare const __CSS_PARSER__: string;
declare const __NS_WEBPACK__: boolean;
declare const __UI_USE_EXTERNAL_RENDERER__: boolean;
declare const __UI_USE_XML_PARSER__: boolean;
declare const __USE_TEST_ID__: boolean | undefined;
declare const __ANDROID__: boolean;
declare const __IOS__: boolean;

View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<item type="id" name="nativescript_accessibility_id"/>
</resources>

View File

@@ -3,7 +3,7 @@ import type { Point, CustomLayoutView as CustomLayoutViewDefinition } from '.';
import type { GestureTypes, GestureEventData } from '../../gestures';
// Types.
import { ViewCommon, isEnabledProperty, originXProperty, originYProperty, isUserInteractionEnabledProperty } from './view-common';
import { ViewCommon, isEnabledProperty, originXProperty, originYProperty, isUserInteractionEnabledProperty, testIDProperty } from './view-common';
import { paddingLeftProperty, paddingTopProperty, paddingRightProperty, paddingBottomProperty, Length } from '../../styling/style-properties';
import { layout } from '../../../utils';
import { Trace } from '../../../trace';
@@ -796,6 +796,23 @@ export class View extends ViewCommon {
this.nativeViewProtected.setAlpha(float(value));
}
[testIDProperty.setNative](value: string) {
this.setTestID(this.nativeViewProtected, value);
}
setTestID(view, value) {
if (typeof __USE_TEST_ID__ !== 'undefined' && __USE_TEST_ID__) {
const id = Utils.ad.resources.getId(':id/nativescript_accessibility_id');
if (id) {
view.setTag(id, value);
view.setTag(value);
}
view.setContentDescription(value);
}
}
[accessibilityEnabledProperty.setNative](value: boolean): void {
this.nativeViewProtected.setFocusable(!!value);
@@ -803,11 +820,15 @@ export class View extends ViewCommon {
}
[accessibilityIdentifierProperty.setNative](value: string): void {
const id = Utils.ad.resources.getId(':id/nativescript_accessibility_id');
if (typeof __USE_TEST_ID__ !== 'undefined' && __USE_TEST_ID__ && this.testID) {
// ignore when using testID;
} else {
const id = Utils.ad.resources.getId(':id/nativescript_accessibility_id');
if (id) {
this.nativeViewProtected.setTag(id, value);
this.nativeViewProtected.setTag(value);
if (id) {
this.nativeViewProtected.setTag(id, value);
this.nativeViewProtected.setTag(value);
}
}
}

View File

@@ -2,7 +2,7 @@
import { Point, View as ViewDefinition } from '.';
// Requires
import { ViewCommon, isEnabledProperty, originXProperty, originYProperty, isUserInteractionEnabledProperty } from './view-common';
import { ViewCommon, isEnabledProperty, originXProperty, originYProperty, isUserInteractionEnabledProperty, testIDProperty } from './view-common';
import { ShowModalOptions, hiddenProperty } from '../view-base';
import { Trace } from '../../../trace';
import { layout, iOSNativeHelper } from '../../../utils';
@@ -572,6 +572,16 @@ export class View extends ViewCommon implements ViewDefinition {
this.updateOriginPoint(this.originX, value);
}
[testIDProperty.setNative](value: string) {
this.setTestID(this.nativeViewProtected, value);
}
public setTestID(view: any, value: string): void {
if (typeof __USE_TEST_ID__ !== 'undefined' && __USE_TEST_ID__) {
view.accessibilityIdentifier = value;
}
}
[accessibilityEnabledProperty.setNative](value: boolean): void {
this.nativeViewProtected.isAccessibilityElement = !!value;
@@ -581,8 +591,13 @@ export class View extends ViewCommon implements ViewDefinition {
[accessibilityIdentifierProperty.getDefault](): string {
return this.nativeViewProtected.accessibilityLabel;
}
[accessibilityIdentifierProperty.setNative](value: string): void {
this.nativeViewProtected.accessibilityIdentifier = value;
if (typeof __USE_TEST_ID__ !== 'undefined' && __USE_TEST_ID__ && this.testID) {
// ignore when using testID
} else {
this.nativeViewProtected.accessibilityIdentifier = value;
}
}
[accessibilityRoleProperty.setNative](value: AccessibilityRole): void {

View File

@@ -82,6 +82,8 @@ export abstract class ViewCommon extends ViewBase implements ViewDefinition {
public accessibilityValue: string;
public accessibilityHint: string;
public testID: string;
public touchAnimation: boolean | TouchAnimationOptions;
public ignoreTouchAnimation: boolean;
@@ -1119,6 +1121,10 @@ export abstract class ViewCommon extends ViewBase implements ViewDefinition {
public accessibilityScreenChanged(): void {
return;
}
public setTestID(view: any, value: string) {
return;
}
}
export const originXProperty = new Property<ViewCommon, number>({
@@ -1196,6 +1202,11 @@ const ignoreTouchAnimationProperty = new Property<ViewCommon, boolean>({
});
ignoreTouchAnimationProperty.register(ViewCommon);
export const testIDProperty = new Property<ViewCommon, string>({
name: 'testID',
});
testIDProperty.register(ViewCommon);
accessibilityIdentifierProperty.register(ViewCommon);
accessibilityLabelProperty.register(ViewCommon);
accessibilityValueProperty.register(ViewCommon);

View File

@@ -15,6 +15,7 @@ import { layout } from '../../utils';
import { isString, isNullOrUndefined } from '../../utils/types';
import { accessibilityIdentifierProperty } from '../../accessibility/accessibility-properties';
import * as Utils from '../../utils';
import { testIDProperty } from '../../ui/core/view';
export * from './text-base-common';
@@ -443,13 +444,21 @@ export class TextBase extends TextBaseCommon {
org.nativescript.widgets.ViewHelper.setPaddingLeft(this.nativeTextViewProtected, Length.toDevicePixels(value, 0) + Length.toDevicePixels(this.style.borderLeftWidth, 0));
}
[accessibilityIdentifierProperty.setNative](value: string): void {
// we override the default setter to apply it on nativeTextViewProtected
const id = Utils.ad.resources.getId(':id/nativescript_accessibility_id');
[testIDProperty.setNative](value: string): void {
this.setTestID(this.nativeTextViewProtected, value);
}
if (id) {
this.nativeTextViewProtected.setTag(id, value);
this.nativeTextViewProtected.setTag(value);
[accessibilityIdentifierProperty.setNative](value: string): void {
if (typeof __USE_TEST_ID__ !== 'undefined' && __USE_TEST_ID__ && this.testID) {
// ignore when using testID;
} else {
// we override the default setter to apply it on nativeTextViewProtected
const id = Utils.ad.resources.getId(':id/nativescript_accessibility_id');
if (id) {
this.nativeTextViewProtected.setTag(id, value);
this.nativeTextViewProtected.setTag(value);
}
}
}