chore(deps): update copilot to pilot.

Co-authored-by: LironMShemen <[email protected]>
This commit is contained in:
Asaf Korem
2025-03-15 18:22:31 +02:00
co-authored by LironMShemen
parent bada25d461
commit 073773754d
27 changed files with 397 additions and 1151 deletions
+14 -8
View File
@@ -9,7 +9,7 @@
// * Dor Ben Baruch <https://github.com/Dor256>
import { BunyanDebugStreamOptions } from 'bunyan-debug-stream';
import { CopilotFacade, PromptHandler } from "detox-copilot";
import type { Pilot, PromptHandler as _PromptHandler } from '@wix-pilot/detox'
declare global {
namespace Detox {
@@ -446,7 +446,13 @@ declare global {
readonly system: SystemFacade;
readonly copilot: DetoxCopilotFacade;
readonly pilot: PilotFacade;
/**
* @deprecated This API is deprecated and will be removed in the next major version.
* Please use `pilot` instead of `pilot`.
*/
readonly copilot: PilotFacade;
readonly DetoxConstants: {
userNotificationTriggers: {
@@ -1329,17 +1335,17 @@ declare global {
element(systemMatcher: SystemMatcher): IndexableSystemElement;
}
interface DetoxCopilotFacade extends Pick<CopilotFacade, "perform"> {
interface PilotFacade extends Pick<Pilot, "perform" | "autopilot" | "extendAPICatalog"> {
/**
* Initializes the Copilot with the given prompt handler.
* Must be called before any other Copilot methods.
* @note Copilot APIs are still in experimental phase and are subject to changes in the near future.
* Initializes the Pilot with the given prompt handler.
* Must be called before any other Pilot methods.
* @note Wix-Pilot APIs are still in experimental phase and are subject to changes in the near future.
* @param promptHandler The prompt handler to use.
*/
init: (promptHandler: DetoxCopilotPromptHandler) => void;
init: (promptHandler: PromptHandler) => void;
}
interface DetoxCopilotPromptHandler extends PromptHandler {}
type PromptHandler = _PromptHandler;
interface IndexableSystemElement extends SystemElement {
/**
+2
View File
@@ -10,6 +10,7 @@ declare global {
const web: Detox.DetoxExportWrapper['web'];
const system: Detox.DetoxExportWrapper['system'];
const copilot: Detox.DetoxExportWrapper['copilot'];
const pilot: Detox.DetoxExportWrapper['pilot'];
namespace NodeJS {
interface Global {
@@ -22,6 +23,7 @@ declare global {
web: Detox.DetoxExportWrapper['web'];
system: Detox.DetoxExportWrapper['system'];
copilot: Detox.DetoxExportWrapper['copilot'];
pilot: Detox.DetoxExportWrapper['pilot'];
}
}
}
+1 -1
View File
@@ -75,7 +75,7 @@ module.exports = {
'src/DetoxWorker.js',
'src/logger/utils/streamUtils.js',
'src/realms',
'src/copilot',
'src/pilot',
],
resetMocks: true,
resetModules: true,
+2 -1
View File
@@ -68,12 +68,13 @@
"wtfnode": "^0.9.1"
},
"dependencies": {
"@wix-pilot/core": "^3.1.3",
"@wix-pilot/detox": "^1.0.8",
"ajv": "^8.6.3",
"bunyan": "^1.8.12",
"bunyan-debug-stream": "^3.1.0",
"caf": "^15.0.1",
"chalk": "^4.0.0",
"detox-copilot": "^0.0.27",
"execa": "^5.1.1",
"find-up": "^5.0.0",
"fs-extra": "^11.0.0",
+16 -10
View File
@@ -1,12 +1,11 @@
const CAF = require('caf');
const copilot = require('detox-copilot').default;
const _ = require('lodash');
const Client = require('./client/Client');
const DetoxCopilot = require('./copilot/DetoxCopilot');
const environmentFactory = require('./environmentFactory');
const { DetoxRuntimeErrorComposer } = require('./errors');
const { InvocationManager } = require('./invoke');
const DetoxPilot = require('./pilot/DetoxPilot');
const symbols = require('./realms/symbols');
const AsyncEmitter = require('./utils/AsyncEmitter');
const uuid = require('./utils/uuid');
@@ -62,8 +61,8 @@ class DetoxWorker {
this.web = null;
/** @type {Detox.SystemFacade} */
this.system = null;
/** @type {Detox.DetoxCopilotFacade} */
this.copilot = new DetoxCopilot();
/** @type {Detox.PilotFacade} */
this.pilot = null;
this._deviceCookie = null;
@@ -103,7 +102,6 @@ class DetoxWorker {
// @ts-ignore
this._sessionConfig.sessionId = sessionConfig.sessionId || uuid.UUID();
this._runtimeErrorComposer.appsConfig = this._appsConfig;
this._client = new Client(sessionConfig);
this._client.terminateApp = async () => {
// @ts-ignore
@@ -111,6 +109,7 @@ class DetoxWorker {
await this.device.terminateApp();
}
};
this.pilot = new DetoxPilot();
yield this._client.connect();
@@ -163,12 +162,19 @@ class DetoxWorker {
const injectedGlobals = {
...matchers,
device: this.device,
copilot: this.copilot,
pilot: this.pilot,
detox: this,
};
this._injectedGlobalProperties = Object.keys(injectedGlobals);
Object.assign(DetoxWorker.global, injectedGlobals);
Object.defineProperty(DetoxWorker.global, 'copilot', {
get: () => {
console.warn('Warning: "copilot" is deprecated. Please use "pilot" instead.');
return this.pilot;
},
configurable: true,
});
}
// @ts-ignore
@@ -226,8 +232,8 @@ class DetoxWorker {
};
onTestStart = function* (_signal, testSummary){
if (copilot.isInitialized()) {
copilot.start();
if (this.pilot.isInitialized()) {
this.pilot.start();
}
this._validateTestSummary('beforeEach', testSummary);
@@ -258,8 +264,8 @@ class DetoxWorker {
testName: testSummary.fullName,
});
if (copilot.isInitialized()) {
copilot.end(testSummary.status !== 'passed');
if (this.pilot.isInitialized()) {
this.pilot.end(testSummary.status === 'passed');
}
};
+42 -38
View File
@@ -1,7 +1,11 @@
// @ts-nocheck
const testSummaries = require('./artifacts/__mocks__/testSummaries.mock');
const configuration = require('./configuration');
const DetoxPilot = require('./pilot/DetoxPilot');
const Deferred = require('./utils/Deferred');
jest.mock('@wix-pilot/detox', () => ({
DetoxFrameworkDriver: jest.fn(),
}));
jest.mock('./utils/logger');
jest.mock('./client/Client');
@@ -10,16 +14,20 @@ jest.mock('./invoke');
jest.mock('./utils/wrapWithStackTraceCutter');
jest.mock('./environmentFactory');
let mockIsInitialized = false;
jest.mock('detox-copilot', () => ({
default: {
init: jest.fn(),
isInitialized: jest.fn(() => mockIsInitialized),
start: jest.fn(),
end: jest.fn(),
perform: jest.fn(),
},
}));
jest.mock('./pilot/DetoxPilot', () => {
return jest.fn().mockImplementation(() => {
return {
init: jest.fn(),
start: jest.fn(),
perform: jest.fn(),
autopilot: jest.fn(),
extendAPICatalog: jest.fn(),
end: jest.fn(),
isInitialized: jest.fn().mockReturnValue(false),
};
});
});
describe('DetoxWorker', () => {
const fakeCookie = {
@@ -51,7 +59,6 @@ describe('DetoxWorker', () => {
let runtimeDevice;
let Detox;
let detox;
let copilot;
beforeEach(() => {
mockEnvironmentFactories();
@@ -67,8 +74,6 @@ describe('DetoxWorker', () => {
});
beforeEach(async () => {
mockIsInitialized = false;
detoxConfig = await configuration.composeDetoxConfig({
override: {
configurations: {
@@ -100,7 +105,6 @@ describe('DetoxWorker', () => {
[symbols.deallocateDevice]: jest.fn(),
};
copilot = require('detox-copilot').default;
});
describe('when DetoxWorker#init() is called', () => {
@@ -307,23 +311,21 @@ describe('DetoxWorker', () => {
});
});
describe('copilot initialization', () => {
let DetoxCopilot;
describe('pilot initialization', () => {
beforeEach(async () => {
DetoxCopilot = require('./copilot/DetoxCopilot');
await init();
});
it('should assign the DetoxCopilot instance to the copilot property', () => {
expect(detox.copilot).toBeDefined();
expect(detox.copilot).toBeInstanceOf(DetoxCopilot);
it('should assign the DetoxPilot instance to the pilot property', () => {
expect(detox.pilot).toBeDefined();
const detoxPilot = new DetoxPilot();
expect(typeof detox.pilot).toBe(typeof detoxPilot);
});
it('should not initialize the copilot', () => {
expect(copilot.init).not.toHaveBeenCalled();
it('should not initialize the pilot', () => {
expect(detox.pilot.init).not.toHaveBeenCalled();
});
});
});
@@ -349,27 +351,29 @@ describe('DetoxWorker', () => {
});
describe('with a valid test summary', () => {
beforeEach(() => detox.onTestStart(testSummaries.running()));
beforeEach(() => {
detox.onTestStart(testSummaries.running());
});
it('should notify artifacts manager about "testStart', () =>
expect(artifactsManager.onTestStart).toHaveBeenCalledWith(testSummaries.running()));
it('should not start copilot if copilot init was not called', async () => {
it('should not start pilot if pilot init was not called', async () => {
try {
await detox.onTestStart('Test');
} catch {}
expect(copilot.start).not.toHaveBeenCalled();
expect(detox.pilot.start).not.toHaveBeenCalled();
});
it('should start copilot if copilot init was called', async () => {
mockIsInitialized = true;
it('should start pilot if pilot init was called', async () => {
detox.pilot.isInitialized = () => true;
try {
await detox.onTestStart('Test');
} catch {}
expect(copilot.start).toHaveBeenCalled();
expect(detox.pilot.start).toHaveBeenCalled();
});
it('should not relaunch app', async () => {
@@ -401,8 +405,8 @@ describe('DetoxWorker', () => {
it('should notify artifacts manager about "testDone"', () =>
expect(artifactsManager.onTestDone).toHaveBeenCalledWith(testSummaries.passed()));
it('should not end copilot if copilot init was not called', async () => {
expect(copilot.end).not.toHaveBeenCalled();
it('should not end pilot if pilot init was not called', async () => {
expect(detox.pilot.end).not.toHaveBeenCalled();
});
});
@@ -420,20 +424,20 @@ describe('DetoxWorker', () => {
expect(client().dumpPendingRequests).toHaveBeenCalled());
});
it('should end copilot with cache enabled if test has passed', async () => {
mockIsInitialized = true;
it('should end pilot with cache enabled if test has passed', async () => {
detox.pilot.isInitialized = () => true;
await detox.onTestDone(testSummaries.passed());
expect(copilot.end).toHaveBeenCalledWith(false);
expect(detox.pilot.end).toHaveBeenCalledWith(false);
});
it('should end copilot without cache if test has failed', async () => {
mockIsInitialized = true;
it('should end pilot without cache if test has failed', async () => {
detox.pilot.isInitialized = () => true;
await detox.onTestDone(testSummaries.failed());
expect(copilot.end).toHaveBeenCalledWith(true);
expect(detox.pilot.end).toHaveBeenCalledWith(true);
});
});
-21
View File
@@ -1,21 +0,0 @@
const copilot = require('detox-copilot').default;
const detoxCopilotFrameworkDriver = require('./detoxCopilotFrameworkDriver');
/**
* @typedef {Object} Detox.DetoxCopilotFacade
*/
class DetoxCopilot {
init(promptHandler) {
copilot.init({
frameworkDriver: detoxCopilotFrameworkDriver,
promptHandler: promptHandler
});
}
perform(...steps) {
return copilot.perform(...steps);
}
}
module.exports = DetoxCopilot;
-46
View File
@@ -1,46 +0,0 @@
const copilot = require('detox-copilot').default;
const DetoxCopilot = require('./DetoxCopilot');
const detoxCopilotFrameworkDriver = require('./detoxCopilotFrameworkDriver');
jest.mock('detox-copilot', () => ({
default: {
init: jest.fn(),
isInitialized: jest.fn().mockReturnValue(false),
start: jest.fn(),
end: jest.fn(),
perform: jest.fn(),
},
}));
jest.mock('./detoxCopilotFrameworkDriver', () => ({}));
describe('DetoxCopilot', () => {
let detoxCopilot;
const mockPromptHandler = jest.fn();
beforeEach(() => {
detoxCopilot = new DetoxCopilot();
jest.clearAllMocks();
});
describe('init', () => {
it.skip('should initialize copilot with correct parameters', () => {
detoxCopilot.init(mockPromptHandler);
expect(copilot.init).toHaveBeenCalledWith({
frameworkDriver: detoxCopilotFrameworkDriver,
promptHandler: mockPromptHandler,
});
});
});
describe('perform', () => {
it('should call copilot.perform with the given intent', async () => {
const intent = 'test something';
await detoxCopilot.perform(intent);
expect(copilot.perform).toHaveBeenCalledWith(intent);
});
});
});
@@ -1,703 +0,0 @@
// eslint-disable-next-line node/no-extraneous-require
const jestExpect = require('expect').default;
const detox = require('../..');
const detoxCopilotFrameworkDriver = {
apiCatalog: {
context: { ...detox, jestExpect },
categories: [
{
title: 'Matchers',
items: [
{
signature: 'by.id(id: string)',
description: 'Matches elements by their test ID.',
example: "element(by.id('loginButton'))",
guidelines: ['Use test IDs (accessibility identifiers) to uniquely identify elements. This is the best-practice matcher.'],
},
{
signature: 'by.text(text: string)',
description: 'Matches elements by their text (value).',
example: "element(by.text('Login'))",
guidelines: ['Prefer test IDs over text matchers when possible.'],
},
{
signature: 'by.type(type: string)',
description: 'Matches elements by their type.',
example: "element(by.type('RCTTextInput'))",
guidelines: ['Use type matchers as a last resort.'],
},
{
signature: 'atIndex(index: number)',
description: 'Selects the element at the specified index from matched elements.',
example: "element(by.id('listItem')).atIndex(2)",
guidelines: ['Use when multiple elements match the same matcher.'],
},
{
signature: 'by.label(label: string)',
description: 'Match elements with the specified label.',
example: "element(by.label('Tuesday, 1 October'));",
guidelines: ['Use when there are no other identifiers, such as for date pickers to select specific days.'],
},
],
},
{
title: 'Actions',
items: [
{
signature: 'tap(point?: Point2D)',
description: 'Simulates tap on an element.',
example: "await element(by.id('loginButton')).tap();",
},
{
signature: 'longPress(point?: Point2D, duration?: number)',
description: 'Simulates long press on an element.',
example: "await element(by.id('menuItem')).longPress();",
guidelines: ['Tapping on edges of elements might work better when adding a small offset to the point.'],
},
{
signature: 'multiTap(times: number)',
description: 'Simulates multiple taps on an element.',
example: "await element(by.id('tappable')).multiTap(3);",
},
{
signature: 'typeText(text: string)',
description: 'Types text into a text field.',
example: "await element(by.id('usernameInput')).typeText('myusername');",
guidelines: ['Element must be a text input field.'],
},
{
signature: 'replaceText(text: string)',
description: 'Replaces text in a text field.',
example: "await element(by.id('textField')).replaceText('new text');",
},
{
signature: 'clearText()',
description: 'Clears text from a text field.',
example: "await element(by.id('textField')).clearText();",
},
{
signature: 'tapReturnKey()',
description: 'Simulates tapping the return key on the keyboard while the element is focused.',
example: "await element(by.id('textField')).tapReturnKey();",
},
{
signature: 'tapBackspaceKey()',
description: 'Simulates tapping the backspace key on the keyboard while the element is focused.',
example: "await element(by.id('textField')).tapBackspaceKey();",
},
{
signature: 'adjustSliderToPosition(normalizedPosition: number)',
description: 'Adjusts slider to a normalized position between 0 and 1.',
example: "await element(by.id('slider')).adjustSliderToPosition(0.75);",
},
{
signature: 'scroll(offset: number, direction: string, startPositionX?: number, startPositionY?: number)',
description: 'Scrolls an element by an offset in a direction.',
example: "await element(by.id('scrollView')).scroll(100, 'down');",
guidelines: [
'Direction can be "up", "down", "left", or "right".',
'Use `startPositionX` and `startPositionY` to specify the starting point of the scroll gesture.',
'`startPositionX` and `startPositionY` are relative to the element\'s width and height, respectively. with values between 0 and 1.',
'If multiple scroll actions are needed while waiting for an element, use `whileElement()` in conjunction with `waitFor()`.',
],
},
{
signature: 'scrollTo(edge: string, startPositionX?: number, startPositionY?: number)',
description: 'Scrolls to an edge of the element.',
example: "await element(by.id('scrollView')).scrollTo('bottom');",
guidelines: [
'Edge can be "top", "bottom", "left", or "right".',
'Use `startPositionX` and `startPositionY` to specify the starting point of the scroll gesture.',
'`startPositionX` and `startPositionY` are relative to the element\'s width and height, respectively. with values between 0 and 1.',
],
},
{
signature: 'waitFor(element: Matcher).toBeVisible(percent?: number).whileElement(element: Matcher).scroll(offset: number, direction: string)',
description: 'Continuously performs an action while waiting for an expectation to be fulfilled.',
example: `
await waitFor(element(by.text('Load More')))
.toBeVisible()
.whileElement(by.id('scrollView'))
.scroll(50, 'down');`,
guidelines: [
'Used in conjunction with `waitFor()` to perform actions like scrolling while waiting for an element to meet the expectation.',
'The action (e.g., `scroll`) is performed on the element specified in `whileElement()`.',
],
},
{
signature: 'scrollToIndex(index: number)',
description: 'Scrolls to the specified index (Android only).',
example: "await element(by.id('scrollView')).scrollToIndex(10);",
},
{
signature: 'swipe(direction: string, speed?: string, normalizedOffset?: number)',
description: 'Simulates a swipe gesture.',
example: "await element(by.id('scrollView')).swipe('up');",
guidelines: [
'Speed can be "fast" or "slow"; default is "fast".',
'Direction can be "up", "down", "left", or "right".',
'Up swipe scrolls down, left swipe scrolls right.',
],
},
{
signature: 'setColumnToValue(column: number, value: string)',
description: 'Sets picker column to a value (iOS only).',
example: "await element(by.id('pickerView')).setColumnToValue(1, '6');",
},
{
signature: 'setDatePickerDate(dateString: string, dateFormat: string)',
description: 'Sets date picker to a specific date.',
example: "await element(by.id('datePicker')).setDatePickerDate('2023-05-25', 'yyyy-MM-dd');",
},
{
signature: 'performAccessibilityAction(actionName: string)',
description: 'Performs an accessibility action.',
example: "await element(by.id('scrollView')).performAccessibilityAction('activate');",
},
{
signature: 'pinch(scale: number, speed?: string, angle?: number)',
description: 'Simulates a pinch gesture.',
example: "await element(by.id('pinchableView')).pinch(1.1);",
guidelines: ['Scale >1 to zoom in, <1 to zoom out.'],
},
{
signature: 'getAttributes()',
description: 'Retrieves attributes of the element.',
example: `
const attributes = await element(by.text('Tap Me')).getAttributes();
jestExpect(attributes.text).toBe('Tap Me');`,
},
{
signature: 'takeScreenshot(name: string)',
description: 'Captures a screenshot of the element.',
example: "const imagePath = await element(by.id('menuRoot')).takeScreenshot('menu_screenshot');",
},
{
signature: 'longPressAndDrag(duration, normalizedPositionX, normalizedPositionY, targetElement, normalizedTargetPositionX, normalizedTargetPositionY, speed, holdDuration)',
description: `Performs a long press and drags to a target element.
- \`duration\` — the duration to press for, in ms (required)
- \`normalizedPositionX\` — X coordinate of the starting point, relative to the element width (required, a number between 0.0 and 1.0, NaN — choose an optimal value automatically)
- \`normalizedPositionY\` — Y coordinate of the starting point, relative to the element height (required, a number between 0.0 and 1.0, NaN — choose an optimal value automatically)
- \`targetElement\` — the target element to drag to (required)
- \`normalizedTargetPositionX\` — X coordinate of the ending point, relative to the target element width (optional, a number between 0.0 and 1.0, NaN — choose an optimal value automatically)
- \`normalizedTargetPositionY\` — Y coordinate of the ending point, relative to the target element height (optional, a number between 0.0 and 1.0, NaN — choose an optimal value automatically)
- \`speed\` — the speed of the drag (optional, valid input: "fast"/"slow" , default is "fast")
- \`holdDuration\` — the duration before releasing at the end, in ms (optional, default is 1000)`,
example: "await element(by.id('cellId_1')).longPressAndDrag(2000, 0.9, NaN, element(by.id('cellId_6')), 0.9, NaN, 'slow', 0);",
},
],
},
{
title: 'Assertions',
items: [
{
signature: 'toBeVisible(percent?: number)',
description: 'Asserts that the element is visible with at-least the specified percentage. Default percent is 75%.',
example: "await expect(element(by.id('loginButton'))).toBeVisible(38);",
guidelines: [
'Use the default visibility percent unless a different percentage is required.',
'If a percentage value is provided, use the exact percentage required for the test.',
],
},
{
signature: 'toExist()',
description: 'Asserts that the element exists.',
example: "await expect(element(by.id('username'))).toExist();",
},
{
signature: 'toHaveText(text: string)',
description: 'Asserts that the element has the specified text.',
example: "await expect(element(by.id('label'))).toHaveText('Hello, World!');",
},
{
signature: 'toHaveValue(value: string)',
description: 'Asserts that the element has the specified value.',
example: "await expect(element(by.id('slider'))).toHaveValue('0.5');",
},
{
signature: 'toBeFocused()',
description: 'Asserts that the element is focused.',
example: "await expect(element(by.id('emailInput'))).toBeFocused();",
},
{
signature: 'toHaveLabel(label: string)',
description: 'Asserts that the element has the specified accessibility label.',
example: "await expect(element(by.id('submitButton'))).toHaveLabel('Submit');",
},
{
signature: 'toHaveId(id: string)',
description: 'Asserts that the element has the specified accessibility identifier.',
example: "await expect(element(by.text('Submit'))).toHaveId('submitButton');",
},
{
signature: 'toHaveSliderPosition(normalizedPosition: number, tolerance?: number)',
description: 'Asserts that the slider is at a normalized position.',
example: "await expect(element(by.id('slider'))).toHaveSliderPosition(0.75);",
},
{
signature: 'toHaveToggleValue(value: boolean)',
description: 'Asserts that a toggle element is on or off.',
example: "await expect(element(by.id('switch'))).toHaveToggleValue(true);",
},
{
signature: 'withTimeout(timeout: number)',
description: 'Waits until the expectation is resolved or timeout occurs.',
example: "await waitFor(element(by.id('bigButton'))).toBeVisible().withTimeout(2000);",
},
{
signature: 'not',
description: 'Negates the expectation.',
example: "await expect(element(by.id('tinyButton'))).not.toBeVisible();",
},
],
},
{
title: 'Utilities',
items: [
{
signature: 'jestExpect',
description: 'Jest expect utility for additional assertions.',
example: `
jestExpect(2 + 2).toBe(4);
jestExpect('hello').toBe('hello');`,
},
],
},
{
title: 'Device APIs',
items: [
{
signature: 'device.launchApp(params?: object)',
description: `
Launches the app with specified parameters.
**Parameters:**
- \`newInstance\` (boolean): If \`true\`, terminates the app and launches a new instance.
- \`delete\` (boolean): If \`true\`, deletes the app data before launching.
- \`launchArgs\` (object): Additional launch arguments as key-value pairs.
- \`url\` (string): URL to open in the app.
- \`permissions\` (object): Permissions to grant the app. Supported permissions are:
| Permission | Values |
|-----------------|----------------------------|
| **location** | always / inuse / never / unset |
| **contacts** | YES / NO / unset / limited |
| **photos** | YES / NO / unset / limited |
| **calendar** | YES / NO / unset |
| **camera** | YES / NO / unset |
| **medialibrary**| YES / NO / unset |
| **microphone** | YES / NO / unset |
| **motion** | YES / NO / unset |
| **reminders** | YES / NO / unset |
| **siri** | YES / NO / unset |
| **notifications**| YES / NO / unset |
| **health** | YES / NO / unset |
| **homekit** | YES / NO / unset |
| **speech** | YES / NO / unset |
| **faceid** | YES / NO / unset |
| **userTracking**| YES / NO / unset |
`,
example: `
await device.launchApp({ newInstance: true });
await device.launchApp({ newInstance: true, permissions: { notifications: 'YES' } });
await device.launchApp({ launchArgs: { someLaunchArg: 1234 } });`,
guidelines: ['Use minimal parameters necessary for your launch scenario.'],
},
{
signature: 'device.reloadReactNative()',
description: 'Reloads the React Native JS bundle.',
example: 'await device.reloadReactNative();',
},
{
signature: 'device.setOrientation(orientation: string)',
description: 'Rotates the device to the specified orientation.',
example: 'await device.setOrientation("landscape");',
guidelines: ['Orientation can be "portrait" or "landscape".'],
},
{
signature: 'device.setLocation(lat: number, lon: number)',
description: 'Sets the device location.',
example: 'await device.setLocation(37.7749, -122.4194);',
},
{
signature: 'device.takeScreenshot(name?: string)',
description: 'Captures a screenshot of the device.',
example: 'const path = await device.takeScreenshot("home_screen");',
},
{
signature: 'device.getPlatform()',
description: 'Returns the current device platform ("ios" or "android").',
example: 'const platform = device.getPlatform();',
guidelines: ['Use to conditionally execute platform-specific code.'],
},
{
signature: 'device.openURL(url: string)',
description: 'Opens a deeplink within the app, or a URL in the browser.',
example: 'await device.openURL("app://home");',
}
],
},
{
title: 'System APIs (iOS)',
items: [
{
signature: 'system.element(matcher: Matcher)',
description: 'Selects an element within the system UI.',
example: "system.element(by.system.label('Allow')).tap();",
guidelines: [
'Can be used for iOS system alerts and permissions dialogs',
'Always use `system.element()` to interact with system dialog elements (alerts, permission requests).',
'Check the platform with `device.getPlatform()` before using, as it is iOS-specific',
'Permission alerts are part of the system UI, not the app UI. Therefore should be matched with `system.element()`.',
'System dialogs are not part of the app, so they won\'t be found in the app\'s view hierarchy. Identify the relevant system element from the snapshot.',
]
},
{
signature: 'by.system.label(label: string)',
description: 'Matches system elements by their label (text).',
example: "system.element(by.system.label('Dismiss'));",
guidelines: [
'System elements will not be found in the app\'s view hierarchy. Read the text from the snapshot.',
'If no system dialog can be found, throw an error with the relevant message.',
],
},
{
signature: 'by.system.type(type: string)',
description: 'Matches system elements by type.',
example: "system.element(by.system.type('button'));",
},
{
signature: 'tap()',
description: 'Taps on a system element.',
example: "system.element(by.system.label('Allow')).tap();",
},
{
signature: 'toExist()',
description: 'Asserts that the system element exists.',
example: "await expect(system.element(by.system.label('Allow'))).toExist();",
},
{
signature: 'not',
description: 'Negates the expectation for system elements.',
example: "await expect(system.element(by.system.label('Allow'))).not.toExist();",
},
],
},
{
title: 'Web APIs',
items: [
{
signature: 'web.element(matcher: Matcher)',
description: 'Selects an element within a web view (`WKWebView` or `RNCWebView`). Use when there is only one web view on the screen.',
example: `
await web.element(by.web.id('email')).typeText('[email protected]');
await web.element(by.web.id('password')).typeText('password123');
await web.element(by.web.id('login-button')).tap();
`,
guidelines: [
'The web view may take time to load; add a delay using `await new Promise(resolve => setTimeout(resolve, milliseconds));` before the first interaction. This wait should happen only once.',
'After the initial wait, you can interact with web elements without additional delays.',
'Use `by.web.id` matcher when possible for matching web elements, as it is the most reliable.',
'Web APIs can only be used with web elements (within web views). Do not use web APIs for native elements or native APIs for web elements!',
'Confirm that you are targeting a web view before using this method.'
],
},
{
signature: 'web(nativeMatcher: NativeMatcher).element(matcher: Matcher)',
description: 'Selects an element within a specific web view (`WKWebView` or `RNCWebView`) matched by a native matcher. Use when there are multiple web views on the screen.',
example: `
// Wait for the specific web view to appear and load (only once before interacting)
await expect(element(by.id('checkout-webview'))).toBeVisible();
// Interact with elements within a specific web view
const specificWebView = web(by.id('checkout-webview'));
await specificWebView.element(by.web.id('credit-card-number')).typeText('4111111111111111');
await specificWebView.element(by.web.id('expiration-date')).typeText('12/25');
await specificWebView.element(by.web.id('cvv')).typeText('123');
await specificWebView.element(by.web.id('pay-button')).tap();
`,
guidelines: [
'Use this method when multiple web views are present.',
'Ensure the web view is visible and its content is loaded before interacting. This wait should happen only once.',
'After the initial wait, you can interact with elements within the web view without additional delays.',
'Webview must be matched with `web(nativeMatcher)` (e.g., `web(by.id(..))` instead of `element(by.id(..))`).',
'Prefer the basic `web.element()` if only one web view is present on the screen.',
'Confirm that you are targeting a web view before using this method.'
],
},
{
signature: 'web(nativeMatcher: NativeMatcher).atIndex(index: number).element(matcher: Matcher)',
description: 'Selects an element within a specific web view at a given index (iOS only).',
example: `
// Interact with an element within the second web view (index starts from 0)
const secondWebView = web(by.id('webview')).atIndex(1);
await secondWebView.element(by.web.id('search-input')).typeText('Detox Testing');
await secondWebView.element(by.web.id('search-button')).tap();
`,
guidelines: [
'Use when multiple web views with the same identifier are present on iOS.',
'This method is available for iOS only.',
'Check the platform with `device.getPlatform()` before using.',
],
},
{
signature: 'by.web.id(id: string)',
description: 'Matches web elements by their "id" attribute.',
example: `await web.element(by.web.id('search')).tap();`,
guidelines: [
'Prefer `by.web.id` over any other matchers when available.',
],
},
{
signature: 'by.web.className(className: string)',
description: 'Matches web elements by their CSS class name.',
example: `await web.element(by.web.className('btn-login')).tap();`,
},
{
signature: 'by.web.cssSelector(cssSelector: string)',
description: 'Matches web elements using a CSS selector.',
example: `await web.element(by.web.cssSelector('#product-list .product-item[data-id="123"]')).scrollToView();`,
},
{
signature: 'by.web.name(name: string)',
description: 'Matches web elements by their name attribute.',
example: `await web.element(by.web.name('firstName')).typeText('John');`,
},
{
signature: 'by.web.xpath(xpath: string)',
description: 'Matches web elements using an XPath expression.',
example: `await web.element(by.web.xpath('//button[text()="Continue"]')).tap();`,
},
{
signature: 'by.web.href(href: string)',
description: 'Matches web elements by their href attribute.',
example: `await web.element(by.web.href('https://www.example.com/contact')).tap();`,
},
{
signature: 'by.web.hrefContains(href: string)',
description: 'Matches web elements whose href attribute contains the specified string.',
example: `await web.element(by.web.hrefContains('/about')).tap();`,
},
{
signature: 'by.web.tag(tag: string)',
description: 'Matches web elements by their tag name.',
example: `await expect(web.element(by.web.tag('h1'))).toHaveText('Welcome to Our Site');`,
},
{
signature: 'by.web.value(value: string)',
description: 'Matches web elements by their value attribute (iOS only).',
example: `await web.element(by.web.value('Sign In')).tap();`,
guidelines: [
'Available on iOS only.',
],
},
{
signature: 'by.web.label(label: string)',
description: 'Matches web elements by their label (iOS only, supports `asSecured()`).',
example: `await web.element(by.web.label('Next')).tap();`,
guidelines: [
'Available on iOS only.',
'Use when the inner web element has a unique label or aria-label.',
'Can be used to match buttons and input elements inside a web view, by their inner text content.',
],
},
{
signature: 'by.web.type(accessibilityType: string)',
description: 'Matches web elements by accessibility type (iOS only, `asSecured()` only).',
example: `await web.element(by.web.type('textField')).asSecured().typeText('Sample Text');`,
guidelines: [
'Available on iOS only and used with `asSecured()`.',
'Type can be any XCUIElement.ElementType, e.g., "button", "textField".',
],
},
{
signature: 'atIndex(index: number)',
description: 'Selects the web element at the specified index from matched elements.',
example: `await expect(web.element(by.web.tag('h2')).atIndex(1)).toHaveText('Features');`,
guidelines: [
'Use when multiple web elements match the same matcher.',
],
},
{
signature: 'tap()',
description: 'Taps on a web element.',
example: `await web.element(by.web.label('Submit')).tap();`,
guidelines: [
'Supports `asSecured()` on iOS.',
],
},
{
signature: 'typeText(text: string)',
description: 'Types text into a web element.',
example: `await web.element(by.web.id('rich-text-editor')).typeText('This is a test message');`,
guidelines: [
'Supports `asSecured()` on iOS.',
],
},
{
signature: 'replaceText(text: string)',
description: 'Replaces text in a web element.',
example: `await web.element(by.web.id('username')).replaceText('new_user');`,
guidelines: [
'Supports `asSecured()` on iOS.',
],
},
{
signature: 'clearText()',
description: 'Clears text from a web element.',
example: `await web.element(by.web.id('comments')).clearText();`,
guidelines: [
'Supports `asSecured()` on iOS.',
],
},
{
signature: 'selectAllText()',
description: 'Selects all text in a web element.',
example: `await web.element(by.web.id('notes')).selectAllText();`,
},
{
signature: 'getText()',
description: 'Retrieves the text content of a web element.',
example: `
const description = await web.element(by.web.id('product-description')).getText();
jestExpect(description).toContain('This product is made from the finest materials.');
`,
guidelines: [
'Use for assertions on the element\'s text content.',
],
},
{
signature: 'scrollToView()',
description: 'Scrolls the web view to bring the element into view.',
example: `await web.element(by.web.id('contact-section')).scrollToView();`,
},
{
signature: 'focus()',
description: 'Focuses on a web element.',
example: `
await web.element(by.web.id('email-input')).focus();
await web.element(by.web.id('email-input')).typeText('[email protected]');
`,
guidelines: [
'Useful for input fields in a web view that require focus before typing.',
'No need for secured interactions on iOS.',
]
},
{
signature: 'moveCursorToEnd()',
description: 'Moves the input cursor in a web view to the end of the element\'s content.',
example: `
await web.element(by.web.id('message-box')).moveCursorToEnd();
await web.element(by.web.id('message-box')).typeText(' Adding more text.');
`,
},
{
signature: 'runScript(script: string, args?: any[])',
description: 'Runs a JavaScript function on the web view element.',
example: `
// Click an element using a custom script
await web.element(by.web.id('hidden-button')).runScript('el => el.click()');
// Set the value of an input field using a custom script
await web.element(by.web.id('username')).runScript('(el, args) => el.value = args[0]', ['new_user']);
// Get the color of an element
const color = await web.element(by.web.id('header')).runScript(function(el) {
return window.getComputedStyle(el).color;
});
jestExpect(color).toBe('rgb(0, 0, 0)');
// Scroll an element into view if not visible
await web.element(by.web.id('lazy-image')).runScript('el => el.scrollIntoView()');
`,
guidelines: [
'The script can accept additional arguments and return a value.',
'Ensure that arguments and return values are serializable.',
'Useful for custom interactions or retrieving properties.',
'Avoid using this method for simple interactions like tapping or typing.',
],
},
{
signature: 'getCurrentUrl()',
description: 'Retrieves the current URL of the web view.',
example: `
await web.element(by.web.id('link-to-page')).tap();
const currentUrl = await web.element(by.web.tag('body')).getCurrentUrl();
jestExpect(currentUrl).toBe('https://www.example.com/target-page');
`,
guidelines: [
'Must be called from an inner element, not the root web view.',
],
},
{
signature: 'getTitle()',
description: 'Retrieves the title of the web view.',
example: `
const pageTitle = await web.element(by.web.tag('body')).getTitle();
jestExpect(pageTitle).toBe('Dashboard - MyApp');
`,
guidelines: [
'Must be called from an inner element, not the root web view.',
],
},
{
signature: 'toHaveText(text: string)',
description: 'Asserts that the web element has the specified text. Used with `expect()`.',
example: `await expect(web.element(by.web.name('submit-button'))).toHaveText('Submit');`,
guidelines: [
'For web views, the text is the inner text of the element, e.g., `<h1>Welcome to the webpage!</h1>`.',
],
},
{
signature: 'toExist()',
description: 'Asserts that the web element exists. Used with `expect()`.',
example: `await expect(web.element(by.web.id('error-message'))).toExist();`,
guidelines: [
'Verifies the presence of a web element in the DOM.',
'Supports `asSecured()` on iOS.',
],
},
{
signature: 'not',
description: 'Negates the expectation for web elements.',
example: `await expect(web.element(by.web.id('loading-spinner'))).not.toExist();`,
guidelines: [
'Use `not` to assert that an element should not be present.',
'Supports `asSecured()` on iOS.',
],
},
{
signature: 'asSecured()',
description: 'Interacts with secured web views (iOS only).',
example: `await web.element(by.web.label('Confirm')).asSecured().tap();`,
guidelines: [
'Use for web pages with secured protocols when regular interactions fail.',
'Applicable on iOS only with specific APIs.',
],
},
],
},
],
},
captureSnapshotImage: async function () {
const fileName = `snapshot_${Date.now()}.png`;
try {
return await detox.device.takeScreenshot(fileName);
} catch (_error) {
return null;
}
},
captureViewHierarchyString: async function () {
try {
return detox.device.generateViewHierarchyXml();
} catch (_error) {
return 'Unavailable, app is probably not launched yet';
}
},
};
module.exports = detoxCopilotFrameworkDriver;
+49
View File
@@ -0,0 +1,49 @@
const { Pilot } = require('@wix-pilot/core');
const { DetoxFrameworkDriver } = require('@wix-pilot/detox');
const detox = require('../..');
/** @type {Detox.PilotFacade} */
class DetoxPilot {
init(promptHandler) {
this.pilot = new Pilot({
frameworkDriver: new DetoxFrameworkDriver(detox),
promptHandler: promptHandler
});
}
start(){
if (!this.isInitialized()) {
throw new Error('DetoxPilot is not initialized');
}
return this.pilot.start();
}
perform(...steps){
if (!this.isInitialized()) {
throw new Error('DetoxPilot is not initialized');
}
return this.pilot.perform(...steps);
}
autopilot(goal) {
return this.pilot.autopilot(goal);
}
extendAPICatalog(categories, context) {
return this.pilot.extendAPICatalog(categories, context);
}
end(shouldSaveInCache){
if(!this.isInitialized()){
throw new Error('DetoxPilot is not initialized');
}
return this.pilot.end(shouldSaveInCache);
}
isInitialized(){
return !!this.pilot;
}
}
module.exports = DetoxPilot;
+89
View File
@@ -0,0 +1,89 @@
const { Pilot } = require('@wix-pilot/core');
const { DetoxFrameworkDriver } = require('@wix-pilot/detox');
const DetoxPilot = require('./DetoxPilot');
jest.mock('@wix-pilot/core');
jest.mock('@wix-pilot/detox', () => ({
DetoxFrameworkDriver: jest.fn(),
}));
describe('DetoxPilot', () => {
let detoxPilot;
const INTENT = 'test something';
const GOAL = 'test goal';
const mockPromptHandler = jest.fn();
beforeEach(() => {
detoxPilot = new DetoxPilot();
jest.clearAllMocks();
});
describe('init', () => {
it('should initialize pilot with correct parameters', () => {
detoxPilot.init(mockPromptHandler);
expect(Pilot).toHaveBeenCalledWith({
frameworkDriver: expect.any(DetoxFrameworkDriver),
promptHandler: mockPromptHandler,
});
});
});
describe('start', () => {
it('should call pilot.start', async () => {
detoxPilot.init(mockPromptHandler);
await detoxPilot.start();
expect(Pilot.prototype.start).toHaveBeenCalled();
});
});
describe('perform', () => {
it('should call pilot.perform with the given intent', async () => {
detoxPilot.init(mockPromptHandler);
await detoxPilot.perform(INTENT);
expect(Pilot.prototype.perform).toHaveBeenCalledWith(INTENT);
});
it('should throw an error if pilot is not initialized', async () => {
expect(() => detoxPilot.perform(INTENT)).toThrow('DetoxPilot is not initialized');
});
});
describe('autopilot', () => {
it('should call pilot.autopilot with the given goal', async () => {
detoxPilot.init(mockPromptHandler);
await detoxPilot.autopilot(GOAL);
expect(Pilot.prototype.autopilot).toHaveBeenCalledWith(GOAL);
});
});
describe('isInitialized', () => {
it('should return true if pilot is initialized', () => {
detoxPilot.init(mockPromptHandler);
expect(detoxPilot.isInitialized()).toBe(true);
});
it('should return false if pilot is not initialized', () => {
expect(detoxPilot.isInitialized()).toBe(false);
});
});
describe('end', () => {
it('should call pilot.end with the given shouldSaveInCache', async () => {
detoxPilot.init(mockPromptHandler);
await detoxPilot.end(true);
expect(Pilot.prototype.end).toHaveBeenCalledWith(true);
});
it('should throw an error if pilot is not initialized', async () => {
expect(() => detoxPilot.end()).toThrow('DetoxPilot is not initialized');
});
});
});
-151
View File
@@ -1,151 +0,0 @@
{
"{\"step\":\"Restart the React Native environment\",\"previous\":[],\"viewHierarchyHash\":\"d340371e5247833d1e5f25dc56f814bf\"}": "await device.reloadReactNative();",
"{\"step\":\"Go to the Actions screen\",\"previous\":[{\"step\":\"Restart the React Native environment\",\"code\":\"await device.reloadReactNative();\"}],\"viewHierarchyHash\":\"0c23f2637180d3fa462232dade056a3d\"}": "await element(by.label('Actions')).tap();",
"{\"step\":\"Attempt a long press on the \\\"Long Press on Top Left\\\" button outside its bounds\",\"previous\":[{\"step\":\"Restart the React Native environment\",\"code\":\"await device.reloadReactNative();\"},{\"step\":\"Go to the Actions screen\",\"code\":\"await element(by.label('Actions')).tap();\"}],\"viewHierarchyHash\":\"460a852de9b3b44dd6f748a3c6e9a8a6\"}": "await element(by.label('Long Press on Top Left')).longPress(450, 153);",
"{\"step\":\"Attempt a long press on the \\\"Long Press on Top Left\\\" button outside its bounds\",\"previous\":[{\"step\":\"Restart the React Native environment\",\"code\":\"await device.reloadReactNative();\"},{\"step\":\"Go to the Actions screen\",\"code\":\"await element(by.label('Actions')).tap();\"},{\"step\":\"Attempt a long press on the \\\"Long Press on Top Left\\\" button outside its bounds\",\"code\":\"await element(by.label('Long Press on Top Left')).longPress(450, 153);\",\"result\":\"Caught an error while evaluating \\\"Attempt a long press on the \\\"Long Press on Top Left\\\" button outside its bounds\\\", tried with generated code: \\\"await element(by.label('Long Press on Top Left')).longPress(450, 153);\\\". Validate the code against the APIs and hierarchy and continue with a different approach. If can't, return a code that throws a descriptive error.\"}],\"viewHierarchyHash\":\"460a852de9b3b44dd6f748a3c6e9a8a6\"}": "`await element(by.label('Long Press on Top Left')).longPress(-50, 153);`",
"{\"step\":\"Restart the React Native environment\",\"previous\":[],\"viewHierarchyHash\":\"a8e5be7c77c3825297a42128e30ee6e0\"}": "await device.reloadReactNative();",
"{\"step\":\"Go to the Actions screen\",\"previous\":[{\"step\":\"Restart the React Native environment\",\"code\":\"await device.reloadReactNative();\"}],\"viewHierarchyHash\":\"0428bed5f927abb8a5aca7013438fb7e\"}": "element(by.label('Actions')).tap();",
"{\"step\":\"Tap the return key on the keyboard for the text input\",\"previous\":[{\"step\":\"Restart the React Native environment\",\"code\":\"await device.reloadReactNative();\"},{\"step\":\"Go to the Actions screen\",\"code\":\"element(by.label('Actions')).tap();\"}],\"viewHierarchyHash\":\"541ab8849c7397247bfb64e714cc54b1\"}": "throw new Error(\"Multiple text inputs found - please specify which text input should receive the return key tap\");",
"{\"step\":\"Tap the return key on the keyboard for the text input\",\"previous\":[{\"step\":\"Restart the React Native environment\",\"code\":\"await device.reloadReactNative();\"},{\"step\":\"Go to the Actions screen\",\"code\":\"element(by.label('Actions')).tap();\"},{\"step\":\"Tap the return key on the keyboard for the text input\",\"code\":\"throw new Error(\\\"Multiple text inputs found - please specify which text input should receive the return key tap\\\");\",\"result\":\"Caught an error while evaluating \\\"Tap the return key on the keyboard for the text input\\\", tried with generated code: \\\"throw new Error(\\\"Multiple text inputs found - please specify which text input should receive the return key tap\\\");\\\". Validate the code against the APIs and hierarchy and continue with a different approach. If can't, return a code that throws a descriptive error.\"}],\"viewHierarchyHash\":\"541ab8849c7397247bfb64e714cc54b1\"}": "Looking at the view hierarchy and snapshot, I can see there are multiple text input fields (RCTSinglelineTextInputView, RCTMultilineTextInputView). Without a specific identifier to target which text input should receive the return key tap, this would be ambiguous. Therefore, I will throw an error:\n\n`throw new Error(\"Multiple text input fields found - please specify which text input should receive the return key tap\");`",
"{\"step\":\"Restart the React Native environment\",\"previous\":[],\"viewHierarchyHash\":\"05a07b8c938528073adc995fbf3026df\"}": "await device.reloadReactNative();",
"{\"step\":\"The element with text \\\"Text1\\\" can be seen\",\"previous\":[{\"step\":\"Restart the React Native environment\",\"code\":\"await device.reloadReactNative();\"},{\"step\":\"Go to the Actions screen\",\"code\":\"element(by.label('Actions')).tap();\"}],\"viewHierarchyHash\":\"541ab8849c7397247bfb64e714cc54b1\"}": "Based on the provided view hierarchy and snapshot image, I can see the element with text \"Text1\" is visible in a scrollable view. Here's the minimal executable code to verify this:\n\n`await expect(element(by.text('Text1'))).toBeVisible();`",
"{\"step\":\"The element with text \\\"Text1\\\" can be seen\",\"previous\":[{\"step\":\"Restart the React Native environment\",\"code\":\"await device.reloadReactNative();\"},{\"step\":\"Go to the Actions screen\",\"code\":\"element(by.label('Actions')).tap();\"},{\"step\":\"The element with text \\\"Text1\\\" can be seen\",\"code\":\"Based on the provided view hierarchy and snapshot image, I can see the element with text \\\"Text1\\\" is visible in a scrollable view. Here's the minimal executable code to verify this:\\n\\n`await expect(element(by.text('Text1'))).toBeVisible();`\",\"result\":\"Caught an error while evaluating \\\"The element with text \\\"Text1\\\" can be seen\\\", tried with generated code: \\\"Based on the provided view hierarchy and snapshot image, I can see the element with text \\\"Text1\\\" is visible in a scrollable view. Here's the minimal executable code to verify this:\\n\\n`await expect(element(by.text('Text1'))).toBeVisible();`\\\". Validate the code against the APIs and hierarchy and continue with a different approach. If can't, return a code that throws a descriptive error.\"}],\"viewHierarchyHash\":\"541ab8849c7397247bfb64e714cc54b1\"}": "Since I can see in the view hierarchy that \"Text1\" appears as a RCTTextView element within a scrollable container and is visible in the snapshot image, I'll generate the minimal executable code to verify this visibility:\n\n`await expect(element(by.text('Text1'))).toBeVisible();`",
"{\"step\":\"Restart the React Native environment\",\"previous\":[],\"viewHierarchyHash\":\"0c23f2637180d3fa462232dade056a3d\"}": "await element(by.text('Switch Root')).tap();",
"{\"step\":\"Go to the Actions screen\",\"previous\":[{\"step\":\"Restart the React Native environment\",\"code\":\"await element(by.text('Switch Root')).tap();\"}],\"viewHierarchyHash\":\"f2c03b95977e00496c834d4d8c14b770\"}": "throw new Error(\"Unable to find any element to navigate to an 'Actions' screen in the current view hierarchy.\");",
"{\"step\":\"Go to the Actions screen\",\"previous\":[{\"step\":\"Restart the React Native environment\",\"code\":\"await element(by.text('Switch Root')).tap();\"},{\"step\":\"Go to the Actions screen\",\"code\":\"throw new Error(\\\"Unable to find any element to navigate to an 'Actions' screen in the current view hierarchy.\\\");\",\"result\":\"Caught an error while evaluating \\\"Go to the Actions screen\\\", tried with generated code: \\\"throw new Error(\\\"Unable to find any element to navigate to an 'Actions' screen in the current view hierarchy.\\\");\\\". Validate the code against the APIs and hierarchy and continue with a different approach. If can't, return a code that throws a descriptive error.\"}],\"viewHierarchyHash\":\"f2c03b95977e00496c834d4d8c14b770\"}": "throw new Error(\"Unable to find any elements that would navigate to an 'Actions' screen in the current view hierarchy. The view only shows options for switching root components.\");",
"{\"step\":\"Restart the React Native environment\",\"previous\":[],\"viewHierarchyHash\":\"5ae52c57648aaa1fed2d4fb3631c436e\"}": "await device.reloadReactNative();",
"{\"step\":\"The slider is set to 25%\",\"previous\":[{\"step\":\"Restart the React Native environment\",\"code\":\"await device.reloadReactNative();\"},{\"step\":\"Go to the Actions screen\",\"code\":\"element(by.label('Actions')).tap();\"}],\"viewHierarchyHash\":\"541ab8849c7397247bfb64e714cc54b1\"}": "await element(by.id('sliderWithASimpleID')).adjustSliderToPosition(0.25);",
"{\"step\":\"Move the slider to the 75% position\",\"previous\":[{\"step\":\"Restart the React Native environment\",\"code\":\"await device.reloadReactNative();\"},{\"step\":\"Go to the Actions screen\",\"code\":\"element(by.label('Actions')).tap();\"},{\"step\":\"The slider is set to 25%\",\"code\":\"await element(by.id('sliderWithASimpleID')).adjustSliderToPosition(0.25);\"}],\"viewHierarchyHash\":\"541ab8849c7397247bfb64e714cc54b1\"}": "Based on the view hierarchy and snapshot image, I can identify the slider element with ID 'sliderWithASimpleID' which appears to be at 25% position currently. To move it to 75% position, we can use the adjustSliderToPosition method. Here's the minimal executable code:\n\n`await element(by.id('sliderWithASimpleID')).adjustSliderToPosition(0.75);`",
"{\"step\":\"Move the slider to the 75% position\",\"previous\":[{\"step\":\"Restart the React Native environment\",\"code\":\"await device.reloadReactNative();\"},{\"step\":\"Go to the Actions screen\",\"code\":\"element(by.label('Actions')).tap();\"},{\"step\":\"The slider is set to 25%\",\"code\":\"await element(by.id('sliderWithASimpleID')).adjustSliderToPosition(0.25);\"},{\"step\":\"Move the slider to the 75% position\",\"code\":\"Based on the view hierarchy and snapshot image, I can identify the slider element with ID 'sliderWithASimpleID' which appears to be at 25% position currently. To move it to 75% position, we can use the adjustSliderToPosition method. Here's the minimal executable code:\\n\\n`await element(by.id('sliderWithASimpleID')).adjustSliderToPosition(0.75);`\",\"result\":\"Caught an error while evaluating \\\"Move the slider to the 75% position\\\", tried with generated code: \\\"Based on the view hierarchy and snapshot image, I can identify the slider element with ID 'sliderWithASimpleID' which appears to be at 25% position currently. To move it to 75% position, we can use the adjustSliderToPosition method. Here's the minimal executable code:\\n\\n`await element(by.id('sliderWithASimpleID')).adjustSliderToPosition(0.75);`\\\". Validate the code against the APIs and hierarchy and continue with a different approach. If can't, return a code that throws a descriptive error.\"}],\"viewHierarchyHash\":\"541ab8849c7397247bfb64e714cc54b1\"}": "`await element(by.id('sliderWithASimpleID')).adjustSliderToPosition(0.75);`",
"{\"step\":\"The slider value is approximately 75%, give or take 10%\",\"previous\":[{\"step\":\"Restart the React Native environment\",\"code\":\"await device.reloadReactNative();\"},{\"step\":\"Go to the Actions screen\",\"code\":\"element(by.label('Actions')).tap();\"},{\"step\":\"The slider is set to 25%\",\"code\":\"await element(by.id('sliderWithASimpleID')).adjustSliderToPosition(0.25);\"},{\"step\":\"Move the slider to the 75% position\",\"code\":\"`await element(by.id('sliderWithASimpleID')).adjustSliderToPosition(0.75);`\"}],\"viewHierarchyHash\":\"541ab8849c7397247bfb64e714cc54b1\"}": "const sliderPosition = 0.75;\nconst tolerance = 0.1; \nawait expect(element(by.id('sliderWithASimpleID'))).toHaveSliderPosition(sliderPosition, tolerance);",
"{\"step\":\"The slider value is approximately 75%, give or take 10%\",\"previous\":[{\"step\":\"Restart the React Native environment\",\"code\":\"await device.reloadReactNative();\"},{\"step\":\"Go to the Actions screen\",\"code\":\"element(by.label('Actions')).tap();\"},{\"step\":\"The slider is set to 25%\",\"code\":\"await element(by.id('sliderWithASimpleID')).adjustSliderToPosition(0.25);\"},{\"step\":\"Move the slider to the 75% position\",\"code\":\"`await element(by.id('sliderWithASimpleID')).adjustSliderToPosition(0.75);`\"},{\"step\":\"The slider value is approximately 75%, give or take 10%\",\"code\":\"const sliderPosition = 0.75;\\nconst tolerance = 0.1; \\nawait expect(element(by.id('sliderWithASimpleID'))).toHaveSliderPosition(sliderPosition, tolerance);\",\"result\":\"Caught an error while evaluating \\\"The slider value is approximately 75%, give or take 10%\\\", tried with generated code: \\\"const sliderPosition = 0.75;\\nconst tolerance = 0.1; \\nawait expect(element(by.id('sliderWithASimpleID'))).toHaveSliderPosition(sliderPosition, tolerance);\\\". Validate the code against the APIs and hierarchy and continue with a different approach. If can't, return a code that throws a descriptive error.\"}],\"viewHierarchyHash\":\"541ab8849c7397247bfb64e714cc54b1\"}": "const sliderPosition = 0.75;\nconst tolerance = 0.1;\nawait expect(element(by.id('sliderWithASimpleID'))).toHaveSliderPosition(sliderPosition, tolerance);",
"{\"step\":\"Restart the React Native environment\",\"previous\":[],\"viewHierarchyHash\":\"541ab8849c7397247bfb64e714cc54b1\"}": "await device.reloadReactNative();",
"{\"step\":\"The text field UniqueId005 (call it \\\"the first\\\") does not have focus\",\"previous\":[{\"step\":\"Restart the React Native environment\",\"code\":\"await device.reloadReactNative();\"},{\"step\":\"Go to the Actions screen\",\"code\":\"element(by.label('Actions')).tap();\"}],\"viewHierarchyHash\":\"541ab8849c7397247bfb64e714cc54b1\"}": "await expect(element(by.id('UniqueId005'))).not.toBeFocused();",
"{\"step\":\"Text input UniqueId006 (call it \\\"the second\\\") is not currently focused\",\"previous\":[{\"step\":\"Restart the React Native environment\",\"code\":\"await device.reloadReactNative();\"},{\"step\":\"Go to the Actions screen\",\"code\":\"element(by.label('Actions')).tap();\"},{\"step\":\"The text field UniqueId005 (call it \\\"the first\\\") does not have focus\",\"code\":\"await expect(element(by.id('UniqueId005'))).not.toBeFocused();\"}],\"viewHierarchyHash\":\"541ab8849c7397247bfb64e714cc54b1\"}": "await expect(element(by.id('UniqueId006'))).not.toBeFocused();",
"{\"step\":\"Tap to focus on the first text field\",\"previous\":[{\"step\":\"Restart the React Native environment\",\"code\":\"await device.reloadReactNative();\"},{\"step\":\"Go to the Actions screen\",\"code\":\"element(by.label('Actions')).tap();\"},{\"step\":\"The text field UniqueId005 (call it \\\"the first\\\") does not have focus\",\"code\":\"await expect(element(by.id('UniqueId005'))).not.toBeFocused();\"},{\"step\":\"Text input UniqueId006 (call it \\\"the second\\\") is not currently focused\",\"code\":\"await expect(element(by.id('UniqueId006'))).not.toBeFocused();\"}],\"viewHierarchyHash\":\"541ab8849c7397247bfb64e714cc54b1\"}": "Based on the view hierarchy, I can see there are multiple text input fields. The first text field appears to have the ID \"UniqueId937\". Given the intent to tap and focus on the first text field, I can generate the code to perform this action:\n\n`await element(by.id('UniqueId937')).tap();`",
"{\"step\":\"Tap to focus on the first text field\",\"previous\":[{\"step\":\"Restart the React Native environment\",\"code\":\"await device.reloadReactNative();\"},{\"step\":\"Go to the Actions screen\",\"code\":\"element(by.label('Actions')).tap();\"},{\"step\":\"The text field UniqueId005 (call it \\\"the first\\\") does not have focus\",\"code\":\"await expect(element(by.id('UniqueId005'))).not.toBeFocused();\"},{\"step\":\"Text input UniqueId006 (call it \\\"the second\\\") is not currently focused\",\"code\":\"await expect(element(by.id('UniqueId006'))).not.toBeFocused();\"},{\"step\":\"Tap to focus on the first text field\",\"code\":\"Based on the view hierarchy, I can see there are multiple text input fields. The first text field appears to have the ID \\\"UniqueId937\\\". Given the intent to tap and focus on the first text field, I can generate the code to perform this action:\\n\\n`await element(by.id('UniqueId937')).tap();`\",\"result\":\"Caught an error while evaluating \\\"Tap to focus on the first text field\\\", tried with generated code: \\\"Based on the view hierarchy, I can see there are multiple text input fields. The first text field appears to have the ID \\\"UniqueId937\\\". Given the intent to tap and focus on the first text field, I can generate the code to perform this action:\\n\\n`await element(by.id('UniqueId937')).tap();`\\\". Validate the code against the APIs and hierarchy and continue with a different approach. If can't, return a code that throws a descriptive error.\"}],\"viewHierarchyHash\":\"541ab8849c7397247bfb64e714cc54b1\"}": "Based on the view hierarchy, I can identify the first text field with ID \"UniqueId937\". To tap and focus on this field, here's the minimal executable code:\n\n`await element(by.id('UniqueId937')).tap();`",
"{\"step\":\"Do magic to the element\",\"previous\":[{\"step\":\"Restart the React Native environment\",\"code\":\"await device.reloadReactNative();\"},{\"step\":\"Go to the Actions screen\",\"code\":\"element(by.label('Actions')).tap();\"}],\"viewHierarchyHash\":\"541ab8849c7397247bfb64e714cc54b1\"}": "`throw new Error(\"Intent 'Do magic to the element' is ambiguous - please specify which element to interact with and what magic action to perform.\");`",
"{\"step\":\"Restart the React Native state\",\"previous\":[],\"viewHierarchyHash\":\"adba489c08d16fffc2ff8728cc4a216f\"}": "await device.reloadReactNative();",
"{\"step\":\"Navigate to the Sanity screen\",\"previous\":[{\"step\":\"Restart the React Native state\",\"code\":\"await device.reloadReactNative();\"}],\"viewHierarchyHash\":\"0c23f2637180d3fa462232dade056a3d\"}": "Based on the view hierarchy and snapshot image, I can see there's a clearly visible \"Sanity\" menu item that can be interacted with. The element is properly labeled and can be tapped using its label. Here's the minimal code required to navigate to the Sanity screen:\n\n`await element(by.label('Sanity')).tap();`",
"{\"step\":\"Navigate to the Sanity screen\",\"previous\":[{\"step\":\"Restart the React Native state\",\"code\":\"await device.reloadReactNative();\"},{\"step\":\"Navigate to the Sanity screen\",\"code\":\"Based on the view hierarchy and snapshot image, I can see there's a clearly visible \\\"Sanity\\\" menu item that can be interacted with. The element is properly labeled and can be tapped using its label. Here's the minimal code required to navigate to the Sanity screen:\\n\\n`await element(by.label('Sanity')).tap();`\",\"result\":\"Caught an error while evaluating \\\"Navigate to the Sanity screen\\\", tried with generated code: \\\"Based on the view hierarchy and snapshot image, I can see there's a clearly visible \\\"Sanity\\\" menu item that can be interacted with. The element is properly labeled and can be tapped using its label. Here's the minimal code required to navigate to the Sanity screen:\\n\\n`await element(by.label('Sanity')).tap();`\\\". Validate the code against the APIs and hierarchy and continue with a different approach. If can't, return a code that throws a descriptive error.\"}],\"viewHierarchyHash\":\"0c23f2637180d3fa462232dade056a3d\"}": "Based on the view hierarchy and snapshot image, I can see there's a clearly visible \"Sanity\" option that can be tapped. The element has a proper label attribute and is correctly positioned in the scrollable menu list. Here's the minimal code required to navigate to the Sanity screen:\n\n`await element(by.label('Sanity')).tap();`",
"{\"step\":\"Restart the React Native state\",\"previous\":[],\"viewHierarchyHash\":\"0c23f2637180d3fa462232dade056a3d\"}": "await device.reloadReactNative();",
"{\"step\":\"Navigate to the Location screen\",\"previous\":[{\"step\":\"Restart the React Native state\",\"code\":\"await device.reloadReactNative();\"}],\"viewHierarchyHash\":\"0c23f2637180d3fa462232dade056a3d\"}": "await element(by.label('Location')).tap();",
"{\"step\":\"Launch the app with location permission denied\",\"previous\":[{\"step\":\"Restart the React Native state\",\"code\":\"await device.reloadReactNative();\"},{\"step\":\"Navigate to the Location screen\",\"code\":\"await element(by.label('Location')).tap();\"}],\"viewHierarchyHash\":\"8fd1be5767b298916debc3225af2f45c\"}": "await device.launchApp({\n newInstance: true,\n permissions: {\n location: 'never'\n }\n});",
"{\"step\":\"Verify there is an element with the text \\\"Get location\\\"\",\"previous\":[{\"step\":\"Restart the React Native state\",\"code\":\"await device.reloadReactNative();\"},{\"step\":\"Navigate to the Location screen\",\"code\":\"await element(by.label('Location')).tap();\"},{\"step\":\"Launch the app with location permission denied\",\"code\":\"await device.launchApp({\\n newInstance: true,\\n permissions: {\\n location: 'never'\\n }\\n});\"}],\"viewHierarchyHash\":\"0c23f2637180d3fa462232dade056a3d\"}": "Looking at the view hierarchy and snapshot, there is no element with the exact text \"Get location\". The closest match is an element labeled \"Location\". Since the intent asks to verify an element with specific text that does not exist in the current view, I will generate code that throws an informative error:\n\n`throw new Error(\"No element with exact text 'Get location' exists in the current view. Found 'Location' instead.\");`",
"{\"step\":\"Verify there is an element with the text \\\"Get location\\\"\",\"previous\":[{\"step\":\"Restart the React Native state\",\"code\":\"await device.reloadReactNative();\"},{\"step\":\"Navigate to the Location screen\",\"code\":\"await element(by.label('Location')).tap();\"},{\"step\":\"Launch the app with location permission denied\",\"code\":\"await device.launchApp({\\n newInstance: true,\\n permissions: {\\n location: 'never'\\n }\\n});\"},{\"step\":\"Verify there is an element with the text \\\"Get location\\\"\",\"code\":\"Looking at the view hierarchy and snapshot, there is no element with the exact text \\\"Get location\\\". The closest match is an element labeled \\\"Location\\\". Since the intent asks to verify an element with specific text that does not exist in the current view, I will generate code that throws an informative error:\\n\\n`throw new Error(\\\"No element with exact text 'Get location' exists in the current view. Found 'Location' instead.\\\");`\",\"result\":\"Caught an error while evaluating \\\"Verify there is an element with the text \\\"Get location\\\"\\\", tried with generated code: \\\"Looking at the view hierarchy and snapshot, there is no element with the exact text \\\"Get location\\\". The closest match is an element labeled \\\"Location\\\". Since the intent asks to verify an element with specific text that does not exist in the current view, I will generate code that throws an informative error:\\n\\n`throw new Error(\\\"No element with exact text 'Get location' exists in the current view. Found 'Location' instead.\\\");`\\\". Validate the code against the APIs and hierarchy and continue with a different approach. If can't, return a code that throws a descriptive error.\"}],\"viewHierarchyHash\":\"0c23f2637180d3fa462232dade056a3d\"}": "Looking at the view hierarchy and snapshot image, there is no element with the exact text \"Get location\". The closest match appears to be just \"Location\". Since the intent asks to verify text that doesn't exist in the current view, I'll generate code that throws an informative error:\n\n`throw new Error(\"No element with text 'Get location' exists in the current view. Found 'Location' instead.\");`",
"{\"step\":\"Launch the app with location permission just once\",\"previous\":[{\"step\":\"Restart the React Native state\",\"code\":\"await device.reloadReactNative();\"},{\"step\":\"Navigate to the Location screen\",\"code\":\"await element(by.label('Location')).tap();\"}],\"viewHierarchyHash\":\"8fd1be5767b298916debc3225af2f45c\"}": "await device.launchApp({\n newInstance: true,\n permissions: {\n location: 'inuse'\n }\n});",
"{\"step\":\"Verify there is an element with the text \\\"Get location\\\"\",\"previous\":[{\"step\":\"Restart the React Native state\",\"code\":\"await device.reloadReactNative();\"},{\"step\":\"Navigate to the Location screen\",\"code\":\"await element(by.label('Location')).tap();\"},{\"step\":\"Launch the app with location permission just once\",\"code\":\"await device.launchApp({\\n newInstance: true,\\n permissions: {\\n location: 'inuse'\\n }\\n});\"}],\"viewHierarchyHash\":\"0c23f2637180d3fa462232dade056a3d\"}": "Looking at the view hierarchy and snapshot image, there is no element with the exact text \"Get location\". The closest matching text is \"Location\". Since this doesn't match the exact text being verified, I need to generate code that will fail with an appropriate message:\n\n`throw new Error(\"No element with exact text 'Get location' found in view hierarchy. Found similar element with text 'Location' instead.\");`",
"{\"step\":\"Verify there is an element with the text \\\"Get location\\\"\",\"previous\":[{\"step\":\"Restart the React Native state\",\"code\":\"await device.reloadReactNative();\"},{\"step\":\"Navigate to the Location screen\",\"code\":\"await element(by.label('Location')).tap();\"},{\"step\":\"Launch the app with location permission just once\",\"code\":\"await device.launchApp({\\n newInstance: true,\\n permissions: {\\n location: 'inuse'\\n }\\n});\"},{\"step\":\"Verify there is an element with the text \\\"Get location\\\"\",\"code\":\"Looking at the view hierarchy and snapshot image, there is no element with the exact text \\\"Get location\\\". The closest matching text is \\\"Location\\\". Since this doesn't match the exact text being verified, I need to generate code that will fail with an appropriate message:\\n\\n`throw new Error(\\\"No element with exact text 'Get location' found in view hierarchy. Found similar element with text 'Location' instead.\\\");`\",\"result\":\"Caught an error while evaluating \\\"Verify there is an element with the text \\\"Get location\\\"\\\", tried with generated code: \\\"Looking at the view hierarchy and snapshot image, there is no element with the exact text \\\"Get location\\\". The closest matching text is \\\"Location\\\". Since this doesn't match the exact text being verified, I need to generate code that will fail with an appropriate message:\\n\\n`throw new Error(\\\"No element with exact text 'Get location' found in view hierarchy. Found similar element with text 'Location' instead.\\\");`\\\". Validate the code against the APIs and hierarchy and continue with a different approach. If can't, return a code that throws a descriptive error.\"}],\"viewHierarchyHash\":\"0c23f2637180d3fa462232dade056a3d\"}": "`throw new Error(\"No element with exact text 'Get location' found in view hierarchy.\");`",
"{\"step\":\"Tap the get location element\",\"previous\":[{\"step\":\"Restart the React Native state\",\"code\":\"await device.reloadReactNative();\"},{\"step\":\"Navigate to the Location screen\",\"code\":\"await element(by.label('Location')).tap();\"},{\"step\":\"Launch the app with location permission just once\",\"code\":\"await device.launchApp({\\n newInstance: true,\\n permissions: {\\n location: 'inuse'\\n }\\n});\"},{\"step\":\"Verify there is an element with the text \\\"Get location\\\"\",\"code\":\"`throw new Error(\\\"No element with exact text 'Get location' found in view hierarchy.\\\");`\"}],\"viewHierarchyHash\":\"0c23f2637180d3fa462232dade056a3d\"}": "throw new Error(\"No 'get location' element found in view hierarchy after navigating to Location screen.\");",
"{\"step\":\"Tap the get location element\",\"previous\":[{\"step\":\"Restart the React Native state\",\"code\":\"await device.reloadReactNative();\"},{\"step\":\"Navigate to the Location screen\",\"code\":\"await element(by.label('Location')).tap();\"},{\"step\":\"Launch the app with location permission just once\",\"code\":\"await device.launchApp({\\n newInstance: true,\\n permissions: {\\n location: 'inuse'\\n }\\n});\"},{\"step\":\"Verify there is an element with the text \\\"Get location\\\"\",\"code\":\"`throw new Error(\\\"No element with exact text 'Get location' found in view hierarchy.\\\");`\"},{\"step\":\"Tap the get location element\",\"code\":\"throw new Error(\\\"No 'get location' element found in view hierarchy after navigating to Location screen.\\\");\",\"result\":\"Caught an error while evaluating \\\"Tap the get location element\\\", tried with generated code: \\\"throw new Error(\\\"No 'get location' element found in view hierarchy after navigating to Location screen.\\\");\\\". Validate the code against the APIs and hierarchy and continue with a different approach. If can't, return a code that throws a descriptive error.\"}],\"viewHierarchyHash\":\"0c23f2637180d3fa462232dade056a3d\"}": "throw new Error(\"No 'get location' element is visible in the view hierarchy or snapshot after navigating to the Location screen.\");",
"{\"step\":\"Remove the app and start a fresh instance\",\"previous\":[],\"viewHierarchyHash\":\"0c23f2637180d3fa462232dade056a3d\"}": "await device.launchApp({ newInstance: true, delete: true });",
"{\"step\":\"Navigate to the System Dialogs screen\",\"previous\":[{\"step\":\"Remove the app and start a fresh instance\",\"code\":\"await device.launchApp({ newInstance: true, delete: true });\"}],\"viewHierarchyHash\":\"0c23f2637180d3fa462232dade056a3d\"}": "await element(by.text('System Dialogs')).tap();",
"{\"step\":\"Check that the initial permission status is \\\"denied\\\"\",\"previous\":[{\"step\":\"Remove the app and start a fresh instance\",\"code\":\"await device.launchApp({ newInstance: true, delete: true });\"},{\"step\":\"Navigate to the System Dialogs screen\",\"code\":\"await element(by.text('System Dialogs')).tap();\"}],\"viewHierarchyHash\":\"dc87c1f29c2a9f99a9ca732d129c7c27\"}": "await expect(element(by.id('permissionStatus'))).toHaveText('denied');",
"{\"step\":\"Tap the button to request permission\",\"previous\":[{\"step\":\"Remove the app and start a fresh instance\",\"code\":\"await device.launchApp({ newInstance: true, delete: true });\"},{\"step\":\"Navigate to the System Dialogs screen\",\"code\":\"await element(by.text('System Dialogs')).tap();\"},{\"step\":\"Check that the initial permission status is \\\"denied\\\"\",\"code\":\"await expect(element(by.id('permissionStatus'))).toHaveText('denied');\"}],\"viewHierarchyHash\":\"dc87c1f29c2a9f99a9ca732d129c7c27\"}": "await element(by.id('requestPermissionButton')).tap();",
"{\"step\":\"A system dialog appears asking for permission\",\"previous\":[{\"step\":\"Remove the app and start a fresh instance\",\"code\":\"await device.launchApp({ newInstance: true, delete: true });\"},{\"step\":\"Navigate to the System Dialogs screen\",\"code\":\"await element(by.text('System Dialogs')).tap();\"},{\"step\":\"Check that the initial permission status is \\\"denied\\\"\",\"code\":\"await expect(element(by.id('permissionStatus'))).toHaveText('denied');\"},{\"step\":\"Tap the button to request permission\",\"code\":\"await element(by.id('requestPermissionButton')).tap();\"}],\"viewHierarchyHash\":\"dc87c1f29c2a9f99a9ca732d129c7c27\"}": "await expect(system.element(by.system.label('Allow \"example\" to track your activity across other companies' apps and websites?'))).toExist();",
"{\"step\":\"A system dialog appears asking for permission\",\"previous\":[{\"step\":\"Remove the app and start a fresh instance\",\"code\":\"await device.launchApp({ newInstance: true, delete: true });\"},{\"step\":\"Navigate to the System Dialogs screen\",\"code\":\"await element(by.text('System Dialogs')).tap();\"},{\"step\":\"Check that the initial permission status is \\\"denied\\\"\",\"code\":\"await expect(element(by.id('permissionStatus'))).toHaveText('denied');\"},{\"step\":\"Tap the button to request permission\",\"code\":\"await element(by.id('requestPermissionButton')).tap();\"},{\"step\":\"A system dialog appears asking for permission\",\"code\":\"await expect(system.element(by.system.label('Allow \\\"example\\\" to track your activity across other companies' apps and websites?'))).toExist();\",\"result\":\"Caught an error while evaluating \\\"A system dialog appears asking for permission\\\", tried with generated code: \\\"await expect(system.element(by.system.label('Allow \\\"example\\\" to track your activity across other companies' apps and websites?'))).toExist();\\\". Validate the code against the APIs and hierarchy and continue with a different approach. If can't, return a code that throws a descriptive error.\"}],\"viewHierarchyHash\":\"dc87c1f29c2a9f99a9ca732d129c7c27\"}": "await expect(system.element(by.system.label('Allow \"example\" to track your activity across other companies' apps and websites?'))).toExist();",
"{\"step\":\"Remove the app and start a fresh instance\",\"previous\":[],\"viewHierarchyHash\":\"dc87c1f29c2a9f99a9ca732d129c7c27\"}": "await device.launchApp({\n newInstance: true,\n delete: true\n});",
"{\"step\":\"Remove the app and start a fresh instance\",\"previous\":[],\"viewHierarchyHash\":\"7f06906a5eff42c6c923af3a593d0b45\"}": "await device.launchApp({ delete: true, newInstance: true });",
"{\"step\":\"Restart the React Native state\",\"previous\":[],\"viewHierarchyHash\":\"b45b24a55ca7f05110963f70c0d15e82\"}": "await device.reloadReactNative();",
"{\"step\":\"Navigate to the Assertions screen\",\"previous\":[{\"step\":\"Restart the React Native state\",\"code\":\"await device.reloadReactNative();\"}],\"viewHierarchyHash\":\"0c23f2637180d3fa462232dade056a3d\"}": "Based on the view hierarchy and snapshot image, I can see that \"Assertions\" is visible as a clickable text element in the list of options. It can be matched uniquely by its label. Here's the minimal code to navigate to the Assertions screen:\n\n`await element(by.label('Assertions')).tap();`",
"{\"step\":\"Navigate to the Assertions screen\",\"previous\":[{\"step\":\"Restart the React Native state\",\"code\":\"await device.reloadReactNative();\"},{\"step\":\"Navigate to the Assertions screen\",\"code\":\"Based on the view hierarchy and snapshot image, I can see that \\\"Assertions\\\" is visible as a clickable text element in the list of options. It can be matched uniquely by its label. Here's the minimal code to navigate to the Assertions screen:\\n\\n`await element(by.label('Assertions')).tap();`\",\"result\":\"Caught an error while evaluating \\\"Navigate to the Assertions screen\\\", tried with generated code: \\\"Based on the view hierarchy and snapshot image, I can see that \\\"Assertions\\\" is visible as a clickable text element in the list of options. It can be matched uniquely by its label. Here's the minimal code to navigate to the Assertions screen:\\n\\n`await element(by.label('Assertions')).tap();`\\\". Validate the code against the APIs and hierarchy and continue with a different approach. If can't, return a code that throws a descriptive error.\"}],\"viewHierarchyHash\":\"0c23f2637180d3fa462232dade056a3d\"}": "Based on the view hierarchy and snapshot image, I can see that \"Assertions\" is visible as a clickable text element in the list. To navigate to the Assertions screen, we can use the label matcher since the element has a unique label text. Here's the minimal executable code:\n\n`await element(by.label('Assertions')).tap();`",
"{\"step\":\"Restart the React Native state\",\"previous\":[],\"viewHierarchyHash\":\"458e1f8d09b11a1befa07a0a871714c9\"}": "await device.reloadReactNative();",
"{\"step\":\"Navigate to the DatePicker screen\",\"previous\":[{\"step\":\"Restart the React Native state\",\"code\":\"await device.reloadReactNative();\"}],\"viewHierarchyHash\":\"0c23f2637180d3fa462232dade056a3d\"}": "Based on the view hierarchy and snapshot image, I can see that \"DatePicker\" is a text label present in the scrollable list. Since the label is visible and clickable, I can generate the code to navigate to it using the label text as the matcher:\n\n`await element(by.text('DatePicker')).tap();`",
"{\"step\":\"Navigate to the DatePicker screen\",\"previous\":[{\"step\":\"Restart the React Native state\",\"code\":\"await device.reloadReactNative();\"},{\"step\":\"Navigate to the DatePicker screen\",\"code\":\"Based on the view hierarchy and snapshot image, I can see that \\\"DatePicker\\\" is a text label present in the scrollable list. Since the label is visible and clickable, I can generate the code to navigate to it using the label text as the matcher:\\n\\n`await element(by.text('DatePicker')).tap();`\",\"result\":\"Caught an error while evaluating \\\"Navigate to the DatePicker screen\\\", tried with generated code: \\\"Based on the view hierarchy and snapshot image, I can see that \\\"DatePicker\\\" is a text label present in the scrollable list. Since the label is visible and clickable, I can generate the code to navigate to it using the label text as the matcher:\\n\\n`await element(by.text('DatePicker')).tap();`\\\". Validate the code against the APIs and hierarchy and continue with a different approach. If can't, return a code that throws a descriptive error.\"}],\"viewHierarchyHash\":\"0c23f2637180d3fa462232dade056a3d\"}": "Looking at the view hierarchy and snapshot image, I can see that \"DatePicker\" is a clickable text element in the scrollable list. Since it's visible and accessible, I can generate the code to tap on it:\n\n`await element(by.text('DatePicker')).tap();`",
"{\"step\":\"Restart the React Native environment\",\"previous\":[],\"viewHierarchyHash\":\"3f78f5f2ac311cc8fcb92a80a165c8cf\"}": "await device.reloadReactNative();",
"{\"step\":\"Long press the top-most left-most corner of the \\\"Long Press on Top Left\\\" button\",\"previous\":[{\"step\":\"Restart the React Native environment\",\"code\":\"await device.reloadReactNative();\"},{\"step\":\"Go to the Actions screen\",\"code\":\"await element(by.label('Actions')).tap();\"}],\"viewHierarchyHash\":\"460a852de9b3b44dd6f748a3c6e9a8a6\"}": "await element(by.label('Long Press on Top Left')).longPress(1, 1);",
"{\"step\":\"Long press the top-most left-most corner of the \\\"Long Press on Top Left\\\" button\",\"previous\":[{\"step\":\"Restart the React Native environment\",\"code\":\"await device.reloadReactNative();\"},{\"step\":\"Go to the Actions screen\",\"code\":\"await element(by.label('Actions')).tap();\"},{\"step\":\"Long press the top-most left-most corner of the \\\"Long Press on Top Left\\\" button\",\"code\":\"await element(by.label('Long Press on Top Left')).longPress(1, 1);\",\"result\":\"Caught an error while evaluating \\\"Long press the top-most left-most corner of the \\\"Long Press on Top Left\\\" button\\\", tried with generated code: \\\"await element(by.label('Long Press on Top Left')).longPress(1, 1);\\\". Validate the code against the APIs and hierarchy and continue with a different approach. If can't, return a code that throws a descriptive error.\"}],\"viewHierarchyHash\":\"460a852de9b3b44dd6f748a3c6e9a8a6\"}": "await element(by.label('Long Press on Top Left')).longPress(1, 1);",
"{\"step\":\"Restart the React Native environment\",\"previous\":[],\"viewHierarchyHash\":\"460a852de9b3b44dd6f748a3c6e9a8a6\"}": "await device.reloadReactNative();",
"{\"step\":\"Restart the React Native environment\",\"previous\":[],\"viewHierarchyHash\":\"917d731983555d21161d1a7f8973af18\"}": "await device.reloadReactNative();",
"{\"step\":\"Input \\\"test\\\" in the text field\",\"previous\":[{\"step\":\"Restart the React Native environment\",\"code\":\"await device.reloadReactNative();\"},{\"step\":\"Go to the Actions screen\",\"code\":\"element(by.label('Actions')).tap();\"}],\"viewHierarchyHash\":\"541ab8849c7397247bfb64e714cc54b1\"}": "Based on the view hierarchy, I can see multiple text input fields. The intent requires typing \"test\" into \"the text field\" but doesn't specify which one. Looking at the snapshot image and hierarchy, there are several text input fields:\n\n1. A single-line text input field (RCTSinglelineTextInputView) located at y=234\n2. A multiline text input field (RCTMultilineTextInputView) with \"some stuff here..\" text at y=334\n3. Another single-line text input field (RCTSinglelineTextInputView) at y=384\n\nSince the intent doesn't specify which text field to target, I'll throw an error requesting clarification:\n\n`throw new Error(\"Multiple text input fields found in the view. Please specify which text field to target.\");`",
"{\"step\":\"Input \\\"test\\\" in the text field\",\"previous\":[{\"step\":\"Restart the React Native environment\",\"code\":\"await device.reloadReactNative();\"},{\"step\":\"Go to the Actions screen\",\"code\":\"element(by.label('Actions')).tap();\"},{\"step\":\"Input \\\"test\\\" in the text field\",\"code\":\"Based on the view hierarchy, I can see multiple text input fields. The intent requires typing \\\"test\\\" into \\\"the text field\\\" but doesn't specify which one. Looking at the snapshot image and hierarchy, there are several text input fields:\\n\\n1. A single-line text input field (RCTSinglelineTextInputView) located at y=234\\n2. A multiline text input field (RCTMultilineTextInputView) with \\\"some stuff here..\\\" text at y=334\\n3. Another single-line text input field (RCTSinglelineTextInputView) at y=384\\n\\nSince the intent doesn't specify which text field to target, I'll throw an error requesting clarification:\\n\\n`throw new Error(\\\"Multiple text input fields found in the view. Please specify which text field to target.\\\");`\",\"result\":\"Caught an error while evaluating \\\"Input \\\"test\\\" in the text field\\\", tried with generated code: \\\"Based on the view hierarchy, I can see multiple text input fields. The intent requires typing \\\"test\\\" into \\\"the text field\\\" but doesn't specify which one. Looking at the snapshot image and hierarchy, there are several text input fields:\\n\\n1. A single-line text input field (RCTSinglelineTextInputView) located at y=234\\n2. A multiline text input field (RCTMultilineTextInputView) with \\\"some stuff here..\\\" text at y=334\\n3. Another single-line text input field (RCTSinglelineTextInputView) at y=384\\n\\nSince the intent doesn't specify which text field to target, I'll throw an error requesting clarification:\\n\\n`throw new Error(\\\"Multiple text input fields found in the view. Please specify which text field to target.\\\");`\\\". Validate the code against the APIs and hierarchy and continue with a different approach. If can't, return a code that throws a descriptive error.\"}],\"viewHierarchyHash\":\"541ab8849c7397247bfb64e714cc54b1\"}": "throw new Error(\"Multiple text input fields found. Please specify which text field to target.\");",
"{\"step\":\"The slider is set to 25%\",\"previous\":[{\"step\":\"Restart the React Native environment\",\"code\":\"await device.reloadReactNative();\"},{\"step\":\"Go to the Actions screen\",\"code\":\"element(by.label('Actions')).tap();\"},{\"step\":\"The slider is set to 25%\",\"code\":\"await element(by.id('sliderWithASimpleID')).adjustSliderToPosition(0.25);\",\"result\":\"Caught an error while evaluating \\\"The slider is set to 25%\\\", tried with generated code: \\\"await element(by.id('sliderWithASimpleID')).adjustSliderToPosition(0.25);\\\". Validate the code against the APIs and hierarchy and continue with a different approach. If can't, return a code that throws a descriptive error.\"}],\"viewHierarchyHash\":\"541ab8849c7397247bfb64e714cc54b1\"}": "await element(by.id('sliderWithASimpleID')).adjustSliderToPosition(0.25);",
"{\"step\":\"The text field UniqueId005 (call it \\\"the first\\\") does not have focus\",\"previous\":[{\"step\":\"Restart the React Native environment\",\"code\":\"await device.reloadReactNative();\"},{\"step\":\"Go to the Actions screen\",\"code\":\"element(by.label('Actions')).tap();\"},{\"step\":\"The text field UniqueId005 (call it \\\"the first\\\") does not have focus\",\"code\":\"await expect(element(by.id('UniqueId005'))).not.toBeFocused();\",\"result\":\"Caught an error while evaluating \\\"The text field UniqueId005 (call it \\\"the first\\\") does not have focus\\\", tried with generated code: \\\"await expect(element(by.id('UniqueId005'))).not.toBeFocused();\\\". Validate the code against the APIs and hierarchy and continue with a different approach. If can't, return a code that throws a descriptive error.\"}],\"viewHierarchyHash\":\"541ab8849c7397247bfb64e714cc54b1\"}": "await expect(element(by.id('UniqueId005'))).not.toBeFocused();",
"{\"step\":\"Restart the React Native state\",\"previous\":[],\"viewHierarchyHash\":\"b8835bc4182c98f0bb33470103592e88\"}": "await device.reloadReactNative();",
"{\"step\":\"Navigate to the WebView screen\",\"previous\":[{\"step\":\"Restart the React Native state\",\"code\":\"await device.reloadReactNative();\"}],\"viewHierarchyHash\":\"0428bed5f927abb8a5aca7013438fb7e\"}": "await element(by.text('WebView')).tap();",
"{\"step\":\"Enable the second WebView\",\"previous\":[{\"step\":\"Restart the React Native state\",\"code\":\"await device.reloadReactNative();\"},{\"step\":\"Navigate to the WebView screen\",\"code\":\"await element(by.text('WebView')).tap();\"}],\"viewHierarchyHash\":\"b8835bc4182c98f0bb33470103592e88\"}": "await element(by.id('toggle2ndWebviewButton')).tap();",
"{\"step\":\"In the second WebView, verify the headline has the message \\\"This is a dummy webview.\\\"\",\"previous\":[{\"step\":\"Restart the React Native state\",\"code\":\"await device.reloadReactNative();\"},{\"step\":\"Navigate to the WebView screen\",\"code\":\"await element(by.text('WebView')).tap();\"},{\"step\":\"Enable the second WebView\",\"code\":\"await element(by.id('toggle2ndWebviewButton')).tap();\"}],\"viewHierarchyHash\":\"32e8fc9416dc1fc3aee3dbb0d1ce2e32\"}": "await expect(web.element(by.web.id('message'))).toHaveText('This is a dummy webview.');",
"{\"step\":\"In the second WebView, verify the headline has the message \\\"This is a dummy webview.\\\"\",\"previous\":[{\"step\":\"Restart the React Native state\",\"code\":\"await device.reloadReactNative();\"},{\"step\":\"Navigate to the WebView screen\",\"code\":\"await element(by.text('WebView')).tap();\"},{\"step\":\"Enable the second WebView\",\"code\":\"await element(by.id('toggle2ndWebviewButton')).tap();\"},{\"step\":\"In the second WebView, verify the headline has the message \\\"This is a dummy webview.\\\"\",\"code\":\"await expect(web.element(by.web.id('message'))).toHaveText('This is a dummy webview.');\",\"result\":\"Caught an error while evaluating \\\"In the second WebView, verify the headline has the message \\\"This is a dummy webview.\\\"\\\", tried with generated code: \\\"await expect(web.element(by.web.id('message'))).toHaveText('This is a dummy webview.');\\\". Validate the code against the APIs and hierarchy and continue with a different approach. If can't, return a code that throws a descriptive error.\"}],\"viewHierarchyHash\":\"32e8fc9416dc1fc3aee3dbb0d1ce2e32\"}": "`await expect(web.element(by.web.id('message'))).toHaveText('This is a dummy webview.');`",
"{\"step\":\"Hide the second WebView\",\"previous\":[{\"step\":\"Restart the React Native state\",\"code\":\"await device.reloadReactNative();\"},{\"step\":\"Navigate to the WebView screen\",\"code\":\"await element(by.text('WebView')).tap();\"},{\"step\":\"Enable the second WebView\",\"code\":\"await element(by.id('toggle2ndWebviewButton')).tap();\"},{\"step\":\"In the second WebView, verify the headline has the message \\\"This is a dummy webview.\\\"\",\"code\":\"`await expect(web.element(by.web.id('message'))).toHaveText('This is a dummy webview.');`\"}],\"viewHierarchyHash\":\"32e8fc9416dc1fc3aee3dbb0d1ce2e32\"}": "await element(by.id('toggle2ndWebviewButton')).tap();",
"{\"step\":\"Restart the React Native environment\",\"previous\":[],\"viewHierarchyHash\":\"7f840438086fdf188cb1ec5f909a8cfa\"}": "await device.reloadReactNative();",
"{\"step\":\"Go to the Actions screen\",\"previous\":[{\"step\":\"Restart the React Native environment\",\"code\":\"await device.reloadReactNative();\"}],\"viewHierarchyHash\":\"74f6292e255463a732384a5649407ee5\"}": "await element(by.text('Actions')).tap();",
"{\"step\":\"Tap the return key on the keyboard for the text input\",\"previous\":[{\"step\":\"Restart the React Native environment\",\"code\":\"await device.reloadReactNative();\"},{\"step\":\"Go to the Actions screen\",\"code\":\"await element(by.text('Actions')).tap();\"}],\"viewHierarchyHash\":\"bb425e9320101560c151b2d020ce77b7\"}": "throw new Error(\"Multiple text inputs found in view hierarchy. Please specify which text input should receive the return key tap action.\");",
"{\"step\":\"Tap the return key on the keyboard for the text input\",\"previous\":[{\"step\":\"Restart the React Native environment\",\"code\":\"await device.reloadReactNative();\"},{\"step\":\"Go to the Actions screen\",\"code\":\"await element(by.text('Actions')).tap();\"},{\"step\":\"Tap the return key on the keyboard for the text input\",\"code\":\"throw new Error(\\\"Multiple text inputs found in view hierarchy. Please specify which text input should receive the return key tap action.\\\");\",\"result\":\"Caught an error while evaluating \\\"Tap the return key on the keyboard for the text input\\\", tried with generated code: \\\"throw new Error(\\\"Multiple text inputs found in view hierarchy. Please specify which text input should receive the return key tap action.\\\");\\\". Validate the code against the APIs and hierarchy and continue with a different approach. If can't, return a code that throws a descriptive error.\"}],\"viewHierarchyHash\":\"bb425e9320101560c151b2d020ce77b7\"}": "throw new Error(\"Multiple text inputs found in the view hierarchy. Please specify which text input should receive the return key tap action.\");",
"{\"step\":\"Restart the React Native environment\",\"previous\":[],\"viewHierarchyHash\":\"70ca2d5c905bc2b6a32e3cda69a7aa33\"}": "await device.reloadReactNative();",
"{\"step\":\"The element with text \\\"Text1\\\" can be seen\",\"previous\":[{\"step\":\"Restart the React Native environment\",\"code\":\"await device.reloadReactNative();\"},{\"step\":\"Go to the Actions screen\",\"code\":\"await element(by.text('Actions')).tap();\"}],\"viewHierarchyHash\":\"bb425e9320101560c151b2d020ce77b7\"}": "await expect(element(by.text('Text1'))).toBeVisible();",
"{\"step\":\"Swipe the view \\\"ScrollView161\\\" upwards\",\"previous\":[{\"step\":\"Restart the React Native environment\",\"code\":\"await device.reloadReactNative();\"},{\"step\":\"Go to the Actions screen\",\"code\":\"await element(by.text('Actions')).tap();\"},{\"step\":\"The element with text \\\"Text1\\\" can be seen\",\"code\":\"await expect(element(by.text('Text1'))).toBeVisible();\"}],\"viewHierarchyHash\":\"bb425e9320101560c151b2d020ce77b7\"}": "throw new Error(\"Cannot find scroll view with ID 'ScrollView161' in the current view hierarchy.\");",
"{\"step\":\"Swipe the view \\\"ScrollView161\\\" upwards\",\"previous\":[{\"step\":\"Restart the React Native environment\",\"code\":\"await device.reloadReactNative();\"},{\"step\":\"Go to the Actions screen\",\"code\":\"await element(by.text('Actions')).tap();\"},{\"step\":\"The element with text \\\"Text1\\\" can be seen\",\"code\":\"await expect(element(by.text('Text1'))).toBeVisible();\"},{\"step\":\"Swipe the view \\\"ScrollView161\\\" upwards\",\"code\":\"throw new Error(\\\"Cannot find scroll view with ID 'ScrollView161' in the current view hierarchy.\\\");\",\"result\":\"Caught an error while evaluating \\\"Swipe the view \\\"ScrollView161\\\" upwards\\\", tried with generated code: \\\"throw new Error(\\\"Cannot find scroll view with ID 'ScrollView161' in the current view hierarchy.\\\");\\\". Validate the code against the APIs and hierarchy and continue with a different approach. If can't, return a code that throws a descriptive error.\"}],\"viewHierarchyHash\":\"bb425e9320101560c151b2d020ce77b7\"}": "await element(by.id('ScrollView161')).swipe('up');",
"{\"step\":\"The Text1 element is no longer in view\",\"previous\":[{\"step\":\"Restart the React Native environment\",\"code\":\"await device.reloadReactNative();\"},{\"step\":\"Go to the Actions screen\",\"code\":\"await element(by.text('Actions')).tap();\"},{\"step\":\"The element with text \\\"Text1\\\" can be seen\",\"code\":\"await expect(element(by.text('Text1'))).toBeVisible();\"},{\"step\":\"Swipe the view \\\"ScrollView161\\\" upwards\",\"code\":\"await element(by.id('ScrollView161')).swipe('up');\"}],\"viewHierarchyHash\":\"7a647a35084f285172ba19cacdfa312b\"}": "await expect(element(by.text('Text1'))).not.toBeVisible();",
"{\"step\":\"Swipe the element back up until the \\\"Text1\\\" element is visible\",\"previous\":[{\"step\":\"Restart the React Native environment\",\"code\":\"await device.reloadReactNative();\"},{\"step\":\"Go to the Actions screen\",\"code\":\"await element(by.text('Actions')).tap();\"},{\"step\":\"The element with text \\\"Text1\\\" can be seen\",\"code\":\"await expect(element(by.text('Text1'))).toBeVisible();\"},{\"step\":\"Swipe the view \\\"ScrollView161\\\" upwards\",\"code\":\"await element(by.id('ScrollView161')).swipe('up');\"},{\"step\":\"The Text1 element is no longer in view\",\"code\":\"await expect(element(by.text('Text1'))).not.toBeVisible();\"}],\"viewHierarchyHash\":\"7a647a35084f285172ba19cacdfa312b\"}": "await waitFor(element(by.text('Text1')))\n .toBeVisible()\n .whileElement(by.id('ScrollView161'))\n .scroll(100, 'down');",
"{\"step\":\"Swipe the element back up until the \\\"Text1\\\" element is visible\",\"previous\":[{\"step\":\"Restart the React Native environment\",\"code\":\"await device.reloadReactNative();\"},{\"step\":\"Go to the Actions screen\",\"code\":\"await element(by.text('Actions')).tap();\"},{\"step\":\"The element with text \\\"Text1\\\" can be seen\",\"code\":\"await expect(element(by.text('Text1'))).toBeVisible();\"},{\"step\":\"Swipe the view \\\"ScrollView161\\\" upwards\",\"code\":\"await element(by.id('ScrollView161')).swipe('up');\"},{\"step\":\"The Text1 element is no longer in view\",\"code\":\"await expect(element(by.text('Text1'))).not.toBeVisible();\"},{\"step\":\"Swipe the element back up until the \\\"Text1\\\" element is visible\",\"code\":\"await waitFor(element(by.text('Text1')))\\n .toBeVisible()\\n .whileElement(by.id('ScrollView161'))\\n .scroll(100, 'down');\",\"result\":\"Caught an error while evaluating \\\"Swipe the element back up until the \\\"Text1\\\" element is visible\\\", tried with generated code: \\\"await waitFor(element(by.text('Text1')))\\n .toBeVisible()\\n .whileElement(by.id('ScrollView161'))\\n .scroll(100, 'down');\\\". Validate the code against the APIs and hierarchy and continue with a different approach. If can't, return a code that throws a descriptive error.\"}],\"viewHierarchyHash\":\"7a647a35084f285172ba19cacdfa312b\"}": "await waitFor(element(by.text('Text1')))\n .toBeVisible()\n .whileElement(by.id('ScrollView161'))\n .scroll(100, 'down');",
"{\"step\":\"Remove the app and start a fresh instance\",\"previous\":[],\"viewHierarchyHash\":\"b16ef5d51f0f64d6a63d434b26d81efc\"}": "await device.launchApp({ newInstance: true, delete: true });",
"{\"step\":\"Navigate to the System Dialogs screen\",\"previous\":[{\"step\":\"Remove the app and start a fresh instance\",\"code\":\"await device.launchApp({ newInstance: true, delete: true });\"}],\"viewHierarchyHash\":\"b16ef5d51f0f64d6a63d434b26d81efc\"}": "```await element(by.text('System Dialogs')).tap();```",
"{\"step\":\"Navigate to the System Dialogs screen\",\"previous\":[{\"step\":\"Remove the app and start a fresh instance\",\"code\":\"await device.launchApp({ newInstance: true, delete: true });\"},{\"step\":\"Navigate to the System Dialogs screen\",\"code\":\"```await element(by.text('System Dialogs')).tap();```\",\"result\":\"Caught an error while evaluating \\\"Navigate to the System Dialogs screen\\\", tried with generated code: \\\"```await element(by.text('System Dialogs')).tap();```\\\". Validate the code against the APIs and hierarchy and continue with a different approach. If can't, return a code that throws a descriptive error.\"}],\"viewHierarchyHash\":\"b16ef5d51f0f64d6a63d434b26d81efc\"}": "```await element(by.text('System Dialogs')).tap();```",
"{\"step\":\"Restart the React Native state\",\"previous\":[],\"viewHierarchyHash\":\"0ca2c9cbebc7ab6248e6b4db4a70160d\"}": "await device.reloadReactNative();",
"{\"step\":\"Navigate to the DatePicker screen\",\"previous\":[{\"step\":\"Restart the React Native state\",\"code\":\"await device.reloadReactNative();\"}],\"viewHierarchyHash\":\"b16ef5d51f0f64d6a63d434b26d81efc\"}": "await element(by.label('DatePicker')).tap();",
"{\"step\":\"Verify there is an element with the text \\\"Compact Date Picker\\\"\",\"previous\":[{\"step\":\"Restart the React Native state\",\"code\":\"await device.reloadReactNative();\"},{\"step\":\"Navigate to the DatePicker screen\",\"code\":\"await element(by.label('DatePicker')).tap();\"}],\"viewHierarchyHash\":\"338d7b478bd28a5a1c110d54da1748a7\"}": "await expect(element(by.text('Compact Date Picker'))).toExist();",
"{\"step\":\"Tap the element with the text \\\"Compact Date Picker\\\"\",\"previous\":[{\"step\":\"Restart the React Native state\",\"code\":\"await device.reloadReactNative();\"},{\"step\":\"Navigate to the DatePicker screen\",\"code\":\"await element(by.label('DatePicker')).tap();\"},{\"step\":\"Verify there is an element with the text \\\"Compact Date Picker\\\"\",\"code\":\"await expect(element(by.text('Compact Date Picker'))).toExist();\"}],\"viewHierarchyHash\":\"338d7b478bd28a5a1c110d54da1748a7\"}": "await element(by.text('Compact Date Picker')).tap();",
"{\"step\":\"Verify there is an element with the text \\\"Inline Date Picker\\\"\",\"previous\":[{\"step\":\"Restart the React Native state\",\"code\":\"await device.reloadReactNative();\"},{\"step\":\"Navigate to the DatePicker screen\",\"code\":\"await element(by.label('DatePicker')).tap();\"},{\"step\":\"Verify there is an element with the text \\\"Compact Date Picker\\\"\",\"code\":\"await expect(element(by.text('Compact Date Picker'))).toExist();\"},{\"step\":\"Tap the element with the text \\\"Compact Date Picker\\\"\",\"code\":\"await element(by.text('Compact Date Picker')).tap();\"}],\"viewHierarchyHash\":\"a6430970141975bb65460afdcfe68f60\"}": "await expect(element(by.text('Inline Date Picker'))).toExist();",
"{\"step\":\"Verify there is an element with today`s date at the bottom of the screen\",\"previous\":[{\"step\":\"Restart the React Native state\",\"code\":\"await device.reloadReactNative();\"},{\"step\":\"Navigate to the DatePicker screen\",\"code\":\"await element(by.label('DatePicker')).tap();\"},{\"step\":\"Verify there is an element with the text \\\"Compact Date Picker\\\"\",\"code\":\"await expect(element(by.text('Compact Date Picker'))).toExist();\"},{\"step\":\"Tap the element with the text \\\"Compact Date Picker\\\"\",\"code\":\"await element(by.text('Compact Date Picker')).tap();\"},{\"step\":\"Verify there is an element with the text \\\"Inline Date Picker\\\"\",\"code\":\"await expect(element(by.text('Inline Date Picker'))).toExist();\"}],\"viewHierarchyHash\":\"a6430970141975bb65460afdcfe68f60\"}": "await expect(element(by.id('datePicker'))).toExist();\nawait expect(element(by.label('July 2023'))).toBeVisible();\nawait expect(element(by.label('Monday, 31 July'))).toBeVisible(); // Verifies date at bottom of calendar",
"{\"step\":\"Verify there is an element with today`s date at the bottom of the screen\",\"previous\":[{\"step\":\"Restart the React Native state\",\"code\":\"await device.reloadReactNative();\"},{\"step\":\"Navigate to the DatePicker screen\",\"code\":\"await element(by.label('DatePicker')).tap();\"},{\"step\":\"Verify there is an element with the text \\\"Compact Date Picker\\\"\",\"code\":\"await expect(element(by.text('Compact Date Picker'))).toExist();\"},{\"step\":\"Tap the element with the text \\\"Compact Date Picker\\\"\",\"code\":\"await element(by.text('Compact Date Picker')).tap();\"},{\"step\":\"Verify there is an element with the text \\\"Inline Date Picker\\\"\",\"code\":\"await expect(element(by.text('Inline Date Picker'))).toExist();\"},{\"step\":\"Verify there is an element with today`s date at the bottom of the screen\",\"code\":\"await expect(element(by.id('datePicker'))).toExist();\\nawait expect(element(by.label('July 2023'))).toBeVisible();\\nawait expect(element(by.label('Monday, 31 July'))).toBeVisible(); // Verifies date at bottom of calendar\",\"result\":\"Caught an error while evaluating \\\"Verify there is an element with today`s date at the bottom of the screen\\\", tried with generated code: \\\"await expect(element(by.id('datePicker'))).toExist();\\nawait expect(element(by.label('July 2023'))).toBeVisible();\\nawait expect(element(by.label('Monday, 31 July'))).toBeVisible(); // Verifies date at bottom of calendar\\\". Validate the code against the APIs and hierarchy and continue with a different approach. If can't, return a code that throws a descriptive error.\"}],\"viewHierarchyHash\":\"a6430970141975bb65460afdcfe68f60\"}": "await expect(element(by.label('July 2023'))).toBeVisible();\nawait expect(element(by.label('31'))).toBeVisible();",
"{\"step\":\"Restart the React Native state\",\"previous\":[],\"viewHierarchyHash\":\"a6430970141975bb65460afdcfe68f60\"}": "await device.reloadReactNative();",
"{\"step\":\"Tap the element with the text \\\"Inline Date Picker\\\"\",\"previous\":[{\"step\":\"Restart the React Native state\",\"code\":\"await device.reloadReactNative();\"},{\"step\":\"Navigate to the DatePicker screen\",\"code\":\"await element(by.label('DatePicker')).tap();\"},{\"step\":\"Verify there is an element with the text \\\"Compact Date Picker\\\"\",\"code\":\"await expect(element(by.text('Compact Date Picker'))).toExist();\"},{\"step\":\"Tap the element with the text \\\"Compact Date Picker\\\"\",\"code\":\"await element(by.text('Compact Date Picker')).tap();\"},{\"step\":\"Verify there is an element with the text \\\"Inline Date Picker\\\"\",\"code\":\"await expect(element(by.text('Inline Date Picker'))).toExist();\"}],\"viewHierarchyHash\":\"a6430970141975bb65460afdcfe68f60\"}": "await element(by.text('Inline Date Picker')).tap();",
"{\"step\":\"Verify that there is slider element at the bottom of the screen\",\"previous\":[{\"step\":\"Restart the React Native state\",\"code\":\"await device.reloadReactNative();\"},{\"step\":\"Navigate to the DatePicker screen\",\"code\":\"await element(by.label('DatePicker')).tap();\"},{\"step\":\"Verify there is an element with the text \\\"Compact Date Picker\\\"\",\"code\":\"await expect(element(by.text('Compact Date Picker'))).toExist();\"},{\"step\":\"Tap the element with the text \\\"Compact Date Picker\\\"\",\"code\":\"await element(by.text('Compact Date Picker')).tap();\"},{\"step\":\"Verify there is an element with the text \\\"Inline Date Picker\\\"\",\"code\":\"await expect(element(by.text('Inline Date Picker'))).toExist();\"},{\"step\":\"Tap the element with the text \\\"Inline Date Picker\\\"\",\"code\":\"await element(by.text('Inline Date Picker')).tap();\"}],\"viewHierarchyHash\":\"dd6b04249fe6f709c315b5de2b08ec23\"}": "await expect(element(by.id('datePicker'))).toExist();\nconst attributes = await element(by.id('datePicker')).getAttributes();\njestExpect(attributes.y).toBeGreaterThan(500);",
"{\"step\":\"Verify that there is slider element at the bottom of the screen\",\"previous\":[{\"step\":\"Restart the React Native state\",\"code\":\"await device.reloadReactNative();\"},{\"step\":\"Navigate to the DatePicker screen\",\"code\":\"await element(by.label('DatePicker')).tap();\"},{\"step\":\"Verify there is an element with the text \\\"Compact Date Picker\\\"\",\"code\":\"await expect(element(by.text('Compact Date Picker'))).toExist();\"},{\"step\":\"Tap the element with the text \\\"Compact Date Picker\\\"\",\"code\":\"await element(by.text('Compact Date Picker')).tap();\"},{\"step\":\"Verify there is an element with the text \\\"Inline Date Picker\\\"\",\"code\":\"await expect(element(by.text('Inline Date Picker'))).toExist();\"},{\"step\":\"Tap the element with the text \\\"Inline Date Picker\\\"\",\"code\":\"await element(by.text('Inline Date Picker')).tap();\"},{\"step\":\"Verify that there is slider element at the bottom of the screen\",\"code\":\"await expect(element(by.id('datePicker'))).toExist();\\nconst attributes = await element(by.id('datePicker')).getAttributes();\\njestExpect(attributes.y).toBeGreaterThan(500);\",\"result\":\"Caught an error while evaluating \\\"Verify that there is slider element at the bottom of the screen\\\", tried with generated code: \\\"await expect(element(by.id('datePicker'))).toExist();\\nconst attributes = await element(by.id('datePicker')).getAttributes();\\njestExpect(attributes.y).toBeGreaterThan(500);\\\". Validate the code against the APIs and hierarchy and continue with a different approach. If can't, return a code that throws a descriptive error.\"}],\"viewHierarchyHash\":\"dd6b04249fe6f709c315b5de2b08ec23\"}": "await expect(element(by.id('datePicker'))).toExist();\nconst attributes = await element(by.id('datePicker')).getAttributes();\njestExpect(attributes.y).toBeGreaterThan(500);",
"{\"step\":\"Reset react native state\",\"previous\":[],\"viewHierarchyHash\":\"b16ef5d51f0f64d6a63d434b26d81efc\"}": "await element(by.label('Switch Root')).tap();",
"{\"step\":\"Enter the \\\"Shape Matching\\\" game screen\",\"previous\":[{\"step\":\"Reset react native state\",\"code\":\"await element(by.label('Switch Root')).tap();\"}],\"viewHierarchyHash\":\"5d4d366cd4d5e8e92dff8bfcfc171e3a\"}": "throw new Error(\"Unable to find any elements related to 'Shape Matching' game screen in the current view hierarchy. The only visible elements are 'Switch to a new native root' and 'Switch to multiple react roots'.\");",
"{\"step\":\"Enter the \\\"Shape Matching\\\" game screen\",\"previous\":[{\"step\":\"Reset react native state\",\"code\":\"await element(by.label('Switch Root')).tap();\"},{\"step\":\"Enter the \\\"Shape Matching\\\" game screen\",\"code\":\"throw new Error(\\\"Unable to find any elements related to 'Shape Matching' game screen in the current view hierarchy. The only visible elements are 'Switch to a new native root' and 'Switch to multiple react roots'.\\\");\",\"result\":\"Caught an error while evaluating \\\"Enter the \\\"Shape Matching\\\" game screen\\\", tried with generated code: \\\"throw new Error(\\\"Unable to find any elements related to 'Shape Matching' game screen in the current view hierarchy. The only visible elements are 'Switch to a new native root' and 'Switch to multiple react roots'.\\\");\\\". Validate the code against the APIs and hierarchy and continue with a different approach. If can't, return a code that throws a descriptive error.\"}],\"viewHierarchyHash\":\"5d4d366cd4d5e8e92dff8bfcfc171e3a\"}": "throw new Error(\"Unable to find elements related to 'Shape Matching' game screen in the current view hierarchy. The only visible elements are 'Switch to a new native root' and 'Switch to multiple react roots'.\");",
"{\"step\":\"Restart the React Native state\",\"previous\":[],\"viewHierarchyHash\":\"b16ef5d51f0f64d6a63d434b26d81efc\"}": "await device.reloadReactNative();",
"{\"step\":\"Navigate to the Location screen\",\"previous\":[{\"step\":\"Restart the React Native state\",\"code\":\"await device.reloadReactNative();\"}],\"viewHierarchyHash\":\"b16ef5d51f0f64d6a63d434b26d81efc\"}": "await element(by.label('Location')).tap();",
"{\"step\":\"Launch the app with location permission denied\",\"previous\":[{\"step\":\"Restart the React Native state\",\"code\":\"await device.reloadReactNative();\"},{\"step\":\"Navigate to the Location screen\",\"code\":\"await element(by.label('Location')).tap();\"}],\"viewHierarchyHash\":\"e79b58a41e3e4e6b49202372b5c1cc79\"}": "await device.launchApp({\n newInstance: true,\n permissions: { \n location: \"never\"\n }\n});",
"{\"step\":\"Verify there is an element with the text \\\"Get location\\\"\",\"previous\":[{\"step\":\"Restart the React Native state\",\"code\":\"await device.reloadReactNative();\"},{\"step\":\"Navigate to the Location screen\",\"code\":\"await element(by.label('Location')).tap();\"},{\"step\":\"Launch the app with location permission denied\",\"code\":\"await device.launchApp({\\n newInstance: true,\\n permissions: { \\n location: \\\"never\\\"\\n }\\n});\"}],\"viewHierarchyHash\":\"b16ef5d51f0f64d6a63d434b26d81efc\"}": "await expect(element(by.text('Get location'))).toExist();",
"{\"step\":\"Verify there is an element with the text \\\"Get location\\\"\",\"previous\":[{\"step\":\"Restart the React Native state\",\"code\":\"await device.reloadReactNative();\"},{\"step\":\"Navigate to the Location screen\",\"code\":\"await element(by.label('Location')).tap();\"},{\"step\":\"Launch the app with location permission denied\",\"code\":\"await device.launchApp({\\n newInstance: true,\\n permissions: { \\n location: \\\"never\\\"\\n }\\n});\"},{\"step\":\"Verify there is an element with the text \\\"Get location\\\"\",\"code\":\"await expect(element(by.text('Get location'))).toExist();\",\"result\":\"Caught an error while evaluating \\\"Verify there is an element with the text \\\"Get location\\\"\\\", tried with generated code: \\\"await expect(element(by.text('Get location'))).toExist();\\\". Validate the code against the APIs and hierarchy and continue with a different approach. If can't, return a code that throws a descriptive error.\"}],\"viewHierarchyHash\":\"b16ef5d51f0f64d6a63d434b26d81efc\"}": "throw new Error(\"The text 'Get location' is not visible in the current view hierarchy or snapshot image\");",
"{\"step\":\"Launch the app with location permission always\",\"previous\":[{\"step\":\"Restart the React Native state\",\"code\":\"await device.reloadReactNative();\"},{\"step\":\"Navigate to the Location screen\",\"code\":\"await element(by.label('Location')).tap();\"}],\"viewHierarchyHash\":\"e79b58a41e3e4e6b49202372b5c1cc79\"}": "await device.launchApp({ \n newInstance: true,\n permissions: { location: 'always' }\n});",
"{\"step\":\"Verify there is an element with the text \\\"Get location\\\"\",\"previous\":[{\"step\":\"Restart the React Native state\",\"code\":\"await device.reloadReactNative();\"},{\"step\":\"Navigate to the Location screen\",\"code\":\"await element(by.label('Location')).tap();\"},{\"step\":\"Launch the app with location permission always\",\"code\":\"await device.launchApp({ \\n newInstance: true,\\n permissions: { location: 'always' }\\n});\"}],\"viewHierarchyHash\":\"b16ef5d51f0f64d6a63d434b26d81efc\"}": "throw new Error(\"No element with text 'Get location' found in the view hierarchy. Found 'Location' instead.\");",
"{\"step\":\"Verify there is an element with the text \\\"Get location\\\"\",\"previous\":[{\"step\":\"Restart the React Native state\",\"code\":\"await device.reloadReactNative();\"},{\"step\":\"Navigate to the Location screen\",\"code\":\"await element(by.label('Location')).tap();\"},{\"step\":\"Launch the app with location permission always\",\"code\":\"await device.launchApp({ \\n newInstance: true,\\n permissions: { location: 'always' }\\n});\"},{\"step\":\"Verify there is an element with the text \\\"Get location\\\"\",\"code\":\"throw new Error(\\\"No element with text 'Get location' found in the view hierarchy. Found 'Location' instead.\\\");\",\"result\":\"Caught an error while evaluating \\\"Verify there is an element with the text \\\"Get location\\\"\\\", tried with generated code: \\\"throw new Error(\\\"No element with text 'Get location' found in the view hierarchy. Found 'Location' instead.\\\");\\\". Validate the code against the APIs and hierarchy and continue with a different approach. If can't, return a code that throws a descriptive error.\"}],\"viewHierarchyHash\":\"b16ef5d51f0f64d6a63d434b26d81efc\"}": "throw new Error(\"No element with text 'Get location' found in the view hierarchy. Found 'Location' instead.\");",
"{\"step\":\"Launch the app with location permission just once\",\"previous\":[{\"step\":\"Restart the React Native state\",\"code\":\"await device.reloadReactNative();\"},{\"step\":\"Navigate to the Location screen\",\"code\":\"await element(by.label('Location')).tap();\"}],\"viewHierarchyHash\":\"e79b58a41e3e4e6b49202372b5c1cc79\"}": "await device.launchApp({\n newInstance: true,\n permissions: { location: 'inuse' }\n});",
"{\"step\":\"Verify there is an element with the text \\\"Get location\\\"\",\"previous\":[{\"step\":\"Restart the React Native state\",\"code\":\"await device.reloadReactNative();\"},{\"step\":\"Navigate to the Location screen\",\"code\":\"await element(by.label('Location')).tap();\"},{\"step\":\"Launch the app with location permission just once\",\"code\":\"await device.launchApp({\\n newInstance: true,\\n permissions: { location: 'inuse' }\\n});\"}],\"viewHierarchyHash\":\"b16ef5d51f0f64d6a63d434b26d81efc\"}": "await expect(element(by.text('Get location'))).toExist();",
"{\"step\":\"Verify there is an element with the text \\\"Get location\\\"\",\"previous\":[{\"step\":\"Restart the React Native state\",\"code\":\"await device.reloadReactNative();\"},{\"step\":\"Navigate to the Location screen\",\"code\":\"await element(by.label('Location')).tap();\"},{\"step\":\"Launch the app with location permission just once\",\"code\":\"await device.launchApp({\\n newInstance: true,\\n permissions: { location: 'inuse' }\\n});\"},{\"step\":\"Verify there is an element with the text \\\"Get location\\\"\",\"code\":\"await expect(element(by.text('Get location'))).toExist();\",\"result\":\"Caught an error while evaluating \\\"Verify there is an element with the text \\\"Get location\\\"\\\", tried with generated code: \\\"await expect(element(by.text('Get location'))).toExist();\\\". Validate the code against the APIs and hierarchy and continue with a different approach. If can't, return a code that throws a descriptive error.\"}],\"viewHierarchyHash\":\"b16ef5d51f0f64d6a63d434b26d81efc\"}": "throw new Error(\"Element with text 'Get location' is not found in the current view hierarchy\")",
"{\"step\":\"Remove the app and start a fresh instance\",\"previous\":[],\"viewHierarchyHash\":\"a14a74aaf07d6f92a7030643e4ac6895\"}": "await device.launchApp({\n delete: true,\n newInstance: true\n});",
"{\"step\":\"Navigate to the System Dialogs screen\",\"previous\":[{\"step\":\"Remove the app and start a fresh instance\",\"code\":\"await device.launchApp({\\n delete: true,\\n newInstance: true\\n});\"}],\"viewHierarchyHash\":\"a14a74aaf07d6f92a7030643e4ac6895\"}": "await element(by.text('System Dialogs')).tap();",
"{\"step\":\"Check that the initial permission status is \\\"denied\\\"\",\"previous\":[{\"step\":\"Remove the app and start a fresh instance\",\"code\":\"await device.launchApp({\\n delete: true,\\n newInstance: true\\n});\"},{\"step\":\"Navigate to the System Dialogs screen\",\"code\":\"await element(by.text('System Dialogs')).tap();\"}],\"viewHierarchyHash\":\"31263c8ad60867de323197ea33be2402\"}": "await expect(element(by.id('permissionStatus'))).toHaveText('denied');",
"{\"step\":\"Tap the button to request permission\",\"previous\":[{\"step\":\"Remove the app and start a fresh instance\",\"code\":\"await device.launchApp({\\n delete: true,\\n newInstance: true\\n});\"},{\"step\":\"Navigate to the System Dialogs screen\",\"code\":\"await element(by.text('System Dialogs')).tap();\"},{\"step\":\"Check that the initial permission status is \\\"denied\\\"\",\"code\":\"await expect(element(by.id('permissionStatus'))).toHaveText('denied');\"}],\"viewHierarchyHash\":\"31263c8ad60867de323197ea33be2402\"}": "await element(by.id('requestPermissionButton')).tap();",
"{\"step\":\"A system dialog appears asking for permission\",\"previous\":[{\"step\":\"Remove the app and start a fresh instance\",\"code\":\"await device.launchApp({\\n delete: true,\\n newInstance: true\\n});\"},{\"step\":\"Navigate to the System Dialogs screen\",\"code\":\"await element(by.text('System Dialogs')).tap();\"},{\"step\":\"Check that the initial permission status is \\\"denied\\\"\",\"code\":\"await expect(element(by.id('permissionStatus'))).toHaveText('denied');\"},{\"step\":\"Tap the button to request permission\",\"code\":\"await element(by.id('requestPermissionButton')).tap();\"}],\"viewHierarchyHash\":\"227afc5a25375e77c25cfdfc6a1c7fe6\"}": "await expect(system.element(by.system.label('Allow \"example-ci\" to track your activity across other companies' apps and websites?'))).toExist();",
"{\"step\":\"A system dialog appears asking for permission\",\"previous\":[{\"step\":\"Remove the app and start a fresh instance\",\"code\":\"await device.launchApp({\\n delete: true,\\n newInstance: true\\n});\"},{\"step\":\"Navigate to the System Dialogs screen\",\"code\":\"await element(by.text('System Dialogs')).tap();\"},{\"step\":\"Check that the initial permission status is \\\"denied\\\"\",\"code\":\"await expect(element(by.id('permissionStatus'))).toHaveText('denied');\"},{\"step\":\"Tap the button to request permission\",\"code\":\"await element(by.id('requestPermissionButton')).tap();\"},{\"step\":\"A system dialog appears asking for permission\",\"code\":\"await expect(system.element(by.system.label('Allow \\\"example-ci\\\" to track your activity across other companies' apps and websites?'))).toExist();\",\"result\":\"Caught an error while evaluating \\\"A system dialog appears asking for permission\\\", tried with generated code: \\\"await expect(system.element(by.system.label('Allow \\\"example-ci\\\" to track your activity across other companies' apps and websites?'))).toExist();\\\". Validate the code against the APIs and hierarchy and continue with a different approach. If can't, return a code that throws a descriptive error.\"}],\"viewHierarchyHash\":\"b85ab02e88af10d34333cce30397498f\"}": "await expect(system.element(by.system.label('Allow \"example-ci\" to track your activity across other companies' apps and websites?'))).toExist();",
"{\"step\":\"Remove the app and start a fresh instance\",\"previous\":[],\"viewHierarchyHash\":\"b85ab02e88af10d34333cce30397498f\"}": "await device.launchApp({\n newInstance: true,\n delete: true\n});",
"{\"step\":\"Restart the React Native state\",\"previous\":[],\"viewHierarchyHash\":\"66b21c68b8b89f4e8d7c1a733d789ea6\"}": "await device.reloadReactNative();",
"{\"step\":\"Navigate to the Assertions screen\",\"previous\":[{\"step\":\"Restart the React Native state\",\"code\":\"await device.reloadReactNative();\"}],\"viewHierarchyHash\":\"b16ef5d51f0f64d6a63d434b26d81efc\"}": "await element(by.text('Assertions')).tap();",
"{\"step\":\"Find an element with ID \\\"RandomJunk959\\\" in the screen\",\"previous\":[{\"step\":\"Restart the React Native state\",\"code\":\"await device.reloadReactNative();\"},{\"step\":\"Navigate to the Assertions screen\",\"code\":\"await element(by.text('Assertions')).tap();\"}],\"viewHierarchyHash\":\"66b21c68b8b89f4e8d7c1a733d789ea6\"}": "throw new Error(\"Element with ID 'RandomJunk959' not found in view hierarchy.\")",
"{\"step\":\"Verify there is a green text element\",\"previous\":[{\"step\":\"Restart the React Native state\",\"code\":\"await device.reloadReactNative();\"},{\"step\":\"Navigate to the Assertions screen\",\"code\":\"await element(by.text('Assertions')).tap();\"}],\"viewHierarchyHash\":\"66b21c68b8b89f4e8d7c1a733d789ea6\"}": "throw new Error(\"Cannot verify if text is green - color information is not available in the view hierarchy and cannot be visually confirmed from the snapshot.\");",
"{\"step\":\"Verify there is a green text element\",\"previous\":[{\"step\":\"Restart the React Native state\",\"code\":\"await device.reloadReactNative();\"},{\"step\":\"Navigate to the Assertions screen\",\"code\":\"await element(by.text('Assertions')).tap();\"},{\"step\":\"Verify there is a green text element\",\"code\":\"throw new Error(\\\"Cannot verify if text is green - color information is not available in the view hierarchy and cannot be visually confirmed from the snapshot.\\\");\",\"result\":\"Caught an error while evaluating \\\"Verify there is a green text element\\\", tried with generated code: \\\"throw new Error(\\\"Cannot verify if text is green - color information is not available in the view hierarchy and cannot be visually confirmed from the snapshot.\\\");\\\". Validate the code against the APIs and hierarchy and continue with a different approach. If can't, return a code that throws a descriptive error.\"}],\"viewHierarchyHash\":\"66b21c68b8b89f4e8d7c1a733d789ea6\"}": "throw new Error(\"Cannot verify if text is green - color attributes are not available through the testing framework APIs for verification\");",
"{\"step\":\"Restart the React Native state\",\"previous\":[],\"viewHierarchyHash\":\"fa4aa3a376fecd97b5bda46715950392\"}": "await device.reloadReactNative();",
"{\"step\":\"Navigate to the Assertions screen\",\"previous\":[{\"step\":\"Restart the React Native state\",\"code\":\"await device.reloadReactNative();\"}],\"viewHierarchyHash\":\"14a5d7072ac79507dd41984bfcdcf07b\"}": "await element(by.text('Assertions')).tap();",
"{\"step\":\"Verify there is a smiling emoji in the top left corner of the screen\",\"previous\":[{\"step\":\"Restart the React Native state\",\"code\":\"await device.reloadReactNative();\"},{\"step\":\"Navigate to the Assertions screen\",\"code\":\"await element(by.text('Assertions')).tap();\"}],\"viewHierarchyHash\":\"c9baf8fa77bc24c637655565c898ea17\"}": "await expect(element(by.id('smile-emoji'))).toBeVisible();\nawait expect(element(by.id('smile-emoji'))).toHaveText('😊');",
"{\"step\":\"Verify there is a party emoji in the bottom right corner of the screen\",\"previous\":[{\"step\":\"Restart the React Native state\",\"code\":\"await device.reloadReactNative();\"},{\"step\":\"Navigate to the Assertions screen\",\"code\":\"await element(by.text('Assertions')).tap();\"},{\"step\":\"Verify there is a smiling emoji in the top left corner of the screen\",\"code\":\"await expect(element(by.id('smile-emoji'))).toBeVisible();\\nawait expect(element(by.id('smile-emoji'))).toHaveText('😊');\"}],\"viewHierarchyHash\":\"c9baf8fa77bc24c637655565c898ea17\"}": "await expect(element(by.id('celebration-emoji'))).toBeVisible();\nawait expect(element(by.id('celebration-emoji'))).toHaveText('🎉');",
"{\"step\":\"Verify there is a party emoji in the center of the screen\",\"previous\":[{\"step\":\"Restart the React Native state\",\"code\":\"await device.reloadReactNative();\"},{\"step\":\"Navigate to the Assertions screen\",\"code\":\"await element(by.text('Assertions')).tap();\"}],\"viewHierarchyHash\":\"c9baf8fa77bc24c637655565c898ea17\"}": "throw new Error(\"Party emoji (🎉) is not centered on screen - found at coordinates (364,863) which is in the bottom-right corner\");",
"{\"step\":\"Verify there is a party emoji in the center of the screen\",\"previous\":[{\"step\":\"Restart the React Native state\",\"code\":\"await device.reloadReactNative();\"},{\"step\":\"Navigate to the Assertions screen\",\"code\":\"await element(by.text('Assertions')).tap();\"},{\"step\":\"Verify there is a party emoji in the center of the screen\",\"code\":\"throw new Error(\\\"Party emoji (🎉) is not centered on screen - found at coordinates (364,863) which is in the bottom-right corner\\\");\",\"result\":\"Caught an error while evaluating \\\"Verify there is a party emoji in the center of the screen\\\", tried with generated code: \\\"throw new Error(\\\"Party emoji (🎉) is not centered on screen - found at coordinates (364,863) which is in the bottom-right corner\\\");\\\". Validate the code against the APIs and hierarchy and continue with a different approach. If can't, return a code that throws a descriptive error.\"}],\"viewHierarchyHash\":\"c9baf8fa77bc24c637655565c898ea17\"}": "throw new Error(\"Party emoji (🎉) is not centered on screen - found at coordinates (364,863) which is in the bottom-right corner\");",
"{\"step\":\"Verify there is a toggle element under the test \\\"I contain some text\\\"\",\"previous\":[{\"step\":\"Restart the React Native state\",\"code\":\"await device.reloadReactNative();\"},{\"step\":\"Navigate to the Assertions screen\",\"code\":\"await element(by.text('Assertions')).tap();\"}],\"viewHierarchyHash\":\"c9baf8fa77bc24c637655565c898ea17\"}": "await expect(element(by.id('toggle'))).toBeVisible();\nawait expect(element(by.id('main-text'))).toBeVisible();\n\nconst mainTextAttributes = await element(by.id('main-text')).getAttributes();\nconst toggleAttributes = await element(by.id('toggle')).getAttributes();\n\njestExpect(toggleAttributes.y).toBeGreaterThan(mainTextAttributes.y);",
"{\"step\":\"Verify there is a toggle element under the test \\\"I contain some text\\\"\",\"previous\":[{\"step\":\"Restart the React Native state\",\"code\":\"await device.reloadReactNative();\"},{\"step\":\"Navigate to the Assertions screen\",\"code\":\"await element(by.text('Assertions')).tap();\"},{\"step\":\"Verify there is a toggle element under the test \\\"I contain some text\\\"\",\"code\":\"await expect(element(by.id('toggle'))).toBeVisible();\\nawait expect(element(by.id('main-text'))).toBeVisible();\\n\\nconst mainTextAttributes = await element(by.id('main-text')).getAttributes();\\nconst toggleAttributes = await element(by.id('toggle')).getAttributes();\\n\\njestExpect(toggleAttributes.y).toBeGreaterThan(mainTextAttributes.y);\",\"result\":\"Caught an error while evaluating \\\"Verify there is a toggle element under the test \\\"I contain some text\\\"\\\", tried with generated code: \\\"await expect(element(by.id('toggle'))).toBeVisible();\\nawait expect(element(by.id('main-text'))).toBeVisible();\\n\\nconst mainTextAttributes = await element(by.id('main-text')).getAttributes();\\nconst toggleAttributes = await element(by.id('toggle')).getAttributes();\\n\\njestExpect(toggleAttributes.y).toBeGreaterThan(mainTextAttributes.y);\\\". Validate the code against the APIs and hierarchy and continue with a different approach. If can't, return a code that throws a descriptive error.\"}],\"viewHierarchyHash\":\"c9baf8fa77bc24c637655565c898ea17\"}": "await expect(element(by.id('toggle'))).toBeVisible();\nawait expect(element(by.id('main-text'))).toBeVisible();\n\nconst mainTextAttributes = await element(by.id('main-text')).getAttributes();\nconst toggleAttributes = await element(by.id('toggle')).getAttributes();\n\njestExpect(toggleAttributes.y).toBeGreaterThan(mainTextAttributes.y);",
"{\"step\":\"Verify there is a toggle element under the text element \\\"I contain some text\\\"\",\"previous\":[{\"step\":\"Restart the React Native state\",\"code\":\"await device.reloadReactNative();\"},{\"step\":\"Navigate to the Assertions screen\",\"code\":\"await element(by.text('Assertions')).tap();\"}],\"viewHierarchyHash\":\"c9baf8fa77bc24c637655565c898ea17\"}": "await expect(element(by.text('I contain some text'))).toExist();\nawait expect(element(by.id('toggle'))).toExist();",
"{\"step\":\"Verify the image element is under the smiling face emoji\",\"previous\":[{\"step\":\"Restart the React Native state\",\"code\":\"await device.reloadReactNative();\"},{\"step\":\"Navigate to the Assertions screen\",\"code\":\"await element(by.text('Assertions')).tap();\"},{\"step\":\"Verify there is a toggle element under the text element \\\"I contain some text\\\"\",\"code\":\"await expect(element(by.id('main-text'))).toExist();\\nawait expect(element(by.id('toggle'))).toExist();\"}],\"viewHierarchyHash\":\"c9baf8fa77bc24c637655565c898ea17\"}": "await expect(element(by.id('smile-emoji'))).toExist();\nawait expect(element(by.id('example-image'))).toExist();\n\nconst smileAttributes = await element(by.id('smile-emoji')).getAttributes();\nconst imageAttributes = await element(by.id('example-image')).getAttributes();\n\nif (smileAttributes.y >= imageAttributes.y) {\n throw new Error('The smiling face emoji is not positioned above the image element');\n}",
"{\"step\":\"Verify the image is under the toggle\",\"previous\":[{\"step\":\"Restart the React Native state\",\"code\":\"await device.reloadReactNative();\"},{\"step\":\"Navigate to the Assertions screen\",\"code\":\"await element(by.text('Assertions')).tap();\"},{\"step\":\"Verify there is a toggle element under the text element \\\"I contain some text\\\"\",\"code\":\"await expect(element(by.id('main-text'))).toExist();\\nawait expect(element(by.id('toggle'))).toExist();\"},{\"step\":\"Verify the image element is under the smiling face emoji\",\"code\":\"await expect(element(by.id('smile-emoji'))).toExist();\\nawait expect(element(by.id('example-image'))).toExist();\\n\\nconst smileAttributes = await element(by.id('smile-emoji')).getAttributes();\\nconst imageAttributes = await element(by.id('example-image')).getAttributes();\\n\\nif (smileAttributes.y >= imageAttributes.y) {\\n throw new Error('The smiling face emoji is not positioned above the image element');\\n}\"}],\"viewHierarchyHash\":\"c9baf8fa77bc24c637655565c898ea17\"}": "const imageAttributes = await element(by.id('example-image')).getAttributes();\nconst toggleAttributes = await element(by.id('toggle')).getAttributes();\n\nif (imageAttributes.y >= toggleAttributes.y) {\n throw new Error('The image element is not positioned above the toggle element');\n}",
"{\"step\":\"Verify the image element is relatively under the smiling face emoji\",\"previous\":[{\"step\":\"Restart the React Native state\",\"code\":\"await device.reloadReactNative();\"},{\"step\":\"Navigate to the Assertions screen\",\"code\":\"await element(by.text('Assertions')).tap();\"},{\"step\":\"Verify there is a toggle element under the text element \\\"I contain some text\\\"\",\"code\":\"await expect(element(by.id('main-text'))).toExist();\\nawait expect(element(by.id('toggle'))).toExist();\"}],\"viewHierarchyHash\":\"c9baf8fa77bc24c637655565c898ea17\"}": "await expect(element(by.id('smile-emoji'))).toExist();\nawait expect(element(by.id('example-image'))).toExist();\nawait expect(element(by.id('smile-emoji'))).toBeVisible();\nawait expect(element(by.id('example-image'))).toBeVisible();\n\nconst smileY = 20;\nconst imageY = 414;\njestExpect(imageY).toBeGreaterThan(smileY);",
"{\"step\":\"Verify the image is under the toggle\",\"previous\":[{\"step\":\"Restart the React Native state\",\"code\":\"await device.reloadReactNative();\"},{\"step\":\"Navigate to the Assertions screen\",\"code\":\"await element(by.text('Assertions')).tap();\"},{\"step\":\"Verify there is a toggle element under the text element \\\"I contain some text\\\"\",\"code\":\"await expect(element(by.id('main-text'))).toExist();\\nawait expect(element(by.id('toggle'))).toExist();\"},{\"step\":\"Verify the image element is relatively under the smiling face emoji\",\"code\":\"await expect(element(by.id('smile-emoji'))).toExist();\\nawait expect(element(by.id('example-image'))).toExist();\\nawait expect(element(by.id('smile-emoji'))).toBeVisible();\\nawait expect(element(by.id('example-image'))).toBeVisible();\\n\\nconst smileY = 20;\\nconst imageY = 414;\\njestExpect(imageY).toBeGreaterThan(smileY);\"}],\"viewHierarchyHash\":\"c9baf8fa77bc24c637655565c898ea17\"}": "await expect(element(by.id('example-image'))).toExist();\nawait expect(element(by.id('toggle'))).toExist();\nawait expect(element(by.id('example-image'))).toBeVisible();\nawait expect(element(by.id('toggle'))).toBeVisible();\n\n// Verify the image appears above the toggle\nconst imageY = 414;\nconst toggleY = 598;\njestExpect(toggleY).toBeGreaterThan(imageY);",
"{\"step\":\"Verify the image element is relatively under the smiling face emoji\",\"previous\":[{\"step\":\"Restart the React Native state\",\"code\":\"await device.reloadReactNative();\"},{\"step\":\"Navigate to the Assertions screen\",\"code\":\"await element(by.text('Assertions')).tap();\"}],\"viewHierarchyHash\":\"c9baf8fa77bc24c637655565c898ea17\"}": "await expect(element(by.id('example-image'))).toExist();\nawait expect(element(by.id('smile-emoji'))).toExist();\nconst imageAttributes = await element(by.id('example-image')).getAttributes();\nconst emojiAttributes = await element(by.id('smile-emoji')).getAttributes();\njestExpect(imageAttributes.y).toBeGreaterThan(emojiAttributes.y);",
"{\"step\":\"Verify the image element is relatively under the smiling face emoji\",\"previous\":[{\"step\":\"Restart the React Native state\",\"code\":\"await device.reloadReactNative();\"},{\"step\":\"Navigate to the Assertions screen\",\"code\":\"await element(by.text('Assertions')).tap();\"},{\"step\":\"Verify the image element is relatively under the smiling face emoji\",\"code\":\"await expect(element(by.id('example-image'))).toExist();\\nawait expect(element(by.id('smile-emoji'))).toExist();\\nconst imageAttributes = await element(by.id('example-image')).getAttributes();\\nconst emojiAttributes = await element(by.id('smile-emoji')).getAttributes();\\njestExpect(imageAttributes.y).toBeGreaterThan(emojiAttributes.y);\",\"result\":\"Caught an error while evaluating \\\"Verify the image element is relatively under the smiling face emoji\\\", tried with generated code: \\\"await expect(element(by.id('example-image'))).toExist();\\nawait expect(element(by.id('smile-emoji'))).toExist();\\nconst imageAttributes = await element(by.id('example-image')).getAttributes();\\nconst emojiAttributes = await element(by.id('smile-emoji')).getAttributes();\\njestExpect(imageAttributes.y).toBeGreaterThan(emojiAttributes.y);\\\". Validate the code against the APIs and hierarchy and continue with a different approach. If can't, return a code that throws a descriptive error.\"}],\"viewHierarchyHash\":\"c9baf8fa77bc24c637655565c898ea17\"}": "await expect(element(by.id('example-image'))).toExist();\nawait expect(element(by.id('smile-emoji'))).toExist();\nconst imageAttributes = await element(by.id('example-image')).getAttributes();\nconst emojiAttributes = await element(by.id('smile-emoji')).getAttributes();\njestExpect(imageAttributes.y).toBeGreaterThan(emojiAttributes.y);",
"{\"step\":\"Verify the image element is under the smiling face emoji element\",\"previous\":[{\"step\":\"Restart the React Native state\",\"code\":\"await device.reloadReactNative();\"},{\"step\":\"Navigate to the Assertions screen\",\"code\":\"await element(by.text('Assertions')).tap();\"}],\"viewHierarchyHash\":\"c9baf8fa77bc24c637655565c898ea17\"}": "// Visual assertion passed: The example-image element (y=414) is positioned below the smile-emoji element (y=20) in the view hierarchy",
"{\"step\":\"Verify the smiling face emoji element is under the image element\",\"previous\":[{\"step\":\"Restart the React Native state\",\"code\":\"await device.reloadReactNative();\"},{\"step\":\"Navigate to the Assertions screen\",\"code\":\"await element(by.text('Assertions')).tap();\"},{\"step\":\"Verify the image element is under the smiling face emoji element\",\"code\":\"// Verify the image element is positioned below the emoji element\\nconst imageYPosition = await element(by.id('example-image')).getAttributes().then(attrs => attrs.frame.y);\\nconst emojiYPosition = await element(by.id('smile-emoji')).getAttributes().then(attrs => attrs.frame.y);\\njestExpect(imageYPosition).toBeGreaterThan(emojiYPosition);\"}],\"viewHierarchyHash\":\"c9baf8fa77bc24c637655565c898ea17\"}": "// Verify the relative positions of elements\nconst emojiYPosition = await element(by.id('smile-emoji')).getAttributes().then(attrs => attrs.frame.y);\nconst imageYPosition = await element(by.id('example-image')).getAttributes().then(attrs => attrs.frame.y);\njestExpect(emojiYPosition).toBeLessThan(imageYPosition);",
"{\"step\":\"Verify the image element is under the smiling face emoji element\",\"previous\":[{\"step\":\"Restart the React Native state\",\"code\":\"await device.reloadReactNative();\"},{\"step\":\"Navigate to the Assertions screen\",\"code\":\"await element(by.text('Assertions')).tap();\"},{\"step\":\"Verify the image element is under the smiling face emoji element\",\"code\":\"const emojiElement = await element(by.id('smile-emoji')).getAttributes();\\nconst imageElement = await element(by.id('example-image')).getAttributes();\\njestExpect(parseInt(imageElement.y)).toBeGreaterThan(parseInt(emojiElement.y));\",\"result\":\"Caught an error while evaluating \\\"Verify the image element is under the smiling face emoji element\\\", tried with generated code: \\\"const emojiElement = await element(by.id('smile-emoji')).getAttributes();\\nconst imageElement = await element(by.id('example-image')).getAttributes();\\njestExpect(parseInt(imageElement.y)).toBeGreaterThan(parseInt(emojiElement.y));\\\". Validate the code against the APIs and hierarchy and continue with a different approach. If can't, return a code that throws a descriptive error.\"}],\"viewHierarchyHash\":\"c9baf8fa77bc24c637655565c898ea17\"}": "const emojiElement = await element(by.id('smile-emoji')).getAttributes();\nconst imageElement = await element(by.id('example-image')).getAttributes();\njestExpect(parseInt(imageElement.y)).toBeGreaterThan(parseInt(emojiElement.y));",
"{\"step\":\"Restart the React Native state\",\"previous\":[],\"viewHierarchyHash\":\"c9baf8fa77bc24c637655565c898ea17\"}": "await device.reloadReactNative();",
"{\"step\":\"Verify the smiling face emoji element is under the image element\",\"previous\":[{\"step\":\"Restart the React Native state\",\"code\":\"await device.reloadReactNative();\"},{\"step\":\"Navigate to the Assertions screen\",\"code\":\"await element(by.text('Assertions')).tap();\"},{\"step\":\"Verify the image element is under the smiling face emoji element\",\"code\":\"await expect(element(by.id('example-image'))).toBeVisible();\\nawait expect(element(by.id('smile-emoji'))).toBeVisible();\\n\\nconst imageAttributes = await element(by.id('example-image')).getAttributes();\\nconst emojiAttributes = await element(by.id('smile-emoji')).getAttributes();\\n\\nif (imageAttributes.y <= emojiAttributes.y) {\\n throw new Error('The image element is not positioned below the smiling face emoji');\\n}\"}],\"viewHierarchyHash\":\"c9baf8fa77bc24c637655565c898ea17\"}": "await expect(element(by.id('smile-emoji'))).toBeVisible();\nawait expect(element(by.id('example-image'))).toBeVisible();\n\nconst emojiAttributes = await element(by.id('smile-emoji')).getAttributes();\nconst imageAttributes = await element(by.id('example-image')).getAttributes();\n\nif (emojiAttributes.y >= imageAttributes.y) {\n throw new Error('The smiling face emoji element is not positioned above the image element');\n}",
"{\"step\":\"Verify the the image element is under smiling face emoji element\",\"previous\":[{\"step\":\"Restart the React Native state\",\"code\":\"await device.reloadReactNative();\"},{\"step\":\"Navigate to the Assertions screen\",\"code\":\"await element(by.text('Assertions')).tap();\"},{\"step\":\"Verify the image element is under the smiling face emoji element\",\"code\":\"await expect(element(by.id('example-image'))).toBeVisible();\\nawait expect(element(by.id('smile-emoji'))).toBeVisible();\\nconst imageAttrs = await element(by.id('example-image')).getAttributes();\\nconst emojiAttrs = await element(by.id('smile-emoji')).getAttributes();\\nif (imageAttrs.y <= emojiAttrs.y) {\\n throw new Error('Image element is not positioned under the smiling face emoji element');\\n}\"}],\"viewHierarchyHash\":\"c9baf8fa77bc24c637655565c898ea17\"}": "await expect(element(by.id('example-image'))).toBeVisible();\nawait expect(element(by.id('smile-emoji'))).toBeVisible();\n\nconst imageAttrs = await element(by.id('example-image')).getAttributes();\nconst emojiAttrs = await element(by.id('smile-emoji')).getAttributes();\nconst imagePos = Number(imageAttrs.y);\nconst emojiPos = Number(emojiAttrs.y);\n\nif (imagePos <= emojiPos) {\n throw new Error('Image element is not positioned below the smiling face emoji element. Image Y: ' + imagePos + ', Emoji Y: ' + emojiPos);\n}",
"{\"step\":\"Verify the the image element is above smiling face emoji element\",\"previous\":[{\"step\":\"Restart the React Native state\",\"code\":\"await device.reloadReactNative();\"},{\"step\":\"Navigate to the Assertions screen\",\"code\":\"await element(by.text('Assertions')).tap();\"},{\"step\":\"Verify the image element is under the smiling face emoji element\",\"code\":\"// Visual assertion passed: The example-image element (y=414) is positioned below the smile-emoji element (y=20) in the view hierarchy\"}],\"viewHierarchyHash\":\"c9baf8fa77bc24c637655565c898ea17\"}": "// Visual assertion failed: The example-image element (y=414) is positioned below the smile-emoji element (y=20) in the view hierarchy. The image element should be above the smiling face emoji for this test to pass.",
"{\"step\":\"Verify there is a blue text element under the image element in the screen\",\"previous\":[{\"step\":\"Restart the React Native state\",\"code\":\"await device.reloadReactNative();\"},{\"step\":\"Navigate to the Assertions screen\",\"code\":\"await element(by.text('Assertions')).tap();\"}],\"viewHierarchyHash\":\"c9baf8fa77bc24c637655565c898ea17\"}": "throw new Error(\"No blue text element found under the image element - found 'Some more text here' and 'I am pink' instead.\");",
"{\"step\":\"Verify there is a blue text element under the image element in the screen\",\"previous\":[{\"step\":\"Restart the React Native state\",\"code\":\"await device.reloadReactNative();\"},{\"step\":\"Navigate to the Assertions screen\",\"code\":\"await element(by.text('Assertions')).tap();\"},{\"step\":\"Verify there is a blue text element under the image element in the screen\",\"code\":\"throw new Error(\\\"No blue text element found under the image element - found 'Some more text here' and 'I am pink' instead.\\\");\",\"result\":\"Caught an error while evaluating \\\"Verify there is a blue text element under the image element in the screen\\\", tried with generated code: \\\"throw new Error(\\\"No blue text element found under the image element - found 'Some more text here' and 'I am pink' instead.\\\");\\\". Validate the code against the APIs and hierarchy and continue with a different approach. If can't, return a code that throws a descriptive error.\"}],\"viewHierarchyHash\":\"c9baf8fa77bc24c637655565c898ea17\"}": "throw new Error(\"No blue text element found under the image element - found 'Some more text here' and 'I am pink' instead.\");",
"{\"step\":\"Verify there is a text element that has blue color\",\"previous\":[{\"step\":\"Restart the React Native state\",\"code\":\"await device.reloadReactNative();\"},{\"step\":\"Navigate to the Assertions screen\",\"code\":\"await element(by.text('Assertions')).tap();\"}],\"viewHierarchyHash\":\"c9baf8fa77bc24c637655565c898ea17\"}": "throw new Error(\"Unable to verify text element color - color information is not available in the view hierarchy and no blue text is visible in the snapshot.\");",
"{\"step\":\"Verify there is a text element that has blue color\",\"previous\":[{\"step\":\"Restart the React Native state\",\"code\":\"await device.reloadReactNative();\"},{\"step\":\"Navigate to the Assertions screen\",\"code\":\"await element(by.text('Assertions')).tap();\"},{\"step\":\"Verify there is a text element that has blue color\",\"code\":\"throw new Error(\\\"Unable to verify text element color - color information is not available in the view hierarchy and no blue text is visible in the snapshot.\\\");\",\"result\":\"Caught an error while evaluating \\\"Verify there is a text element that has blue color\\\", tried with generated code: \\\"throw new Error(\\\"Unable to verify text element color - color information is not available in the view hierarchy and no blue text is visible in the snapshot.\\\");\\\". Validate the code against the APIs and hierarchy and continue with a different approach. If can't, return a code that throws a descriptive error.\"}],\"viewHierarchyHash\":\"c9baf8fa77bc24c637655565c898ea17\"}": "throw new Error(\"Unable to verify text element color - no blue text is visible in the snapshot and color information is not available in the view hierarchy.\");",
"{\"step\":\"Verify there is a blue color text element\",\"previous\":[{\"step\":\"Restart the React Native state\",\"code\":\"await device.reloadReactNative();\"},{\"step\":\"Navigate to the Assertions screen\",\"code\":\"await element(by.text('Assertions')).tap();\"}],\"viewHierarchyHash\":\"c9baf8fa77bc24c637655565c898ea17\"}": "throw new Error(\"No blue colored text element found in the current view hierarchy or snapshot image.\");",
"{\"step\":\"Verify there is a blue color text element\",\"previous\":[{\"step\":\"Restart the React Native state\",\"code\":\"await device.reloadReactNative();\"},{\"step\":\"Navigate to the Assertions screen\",\"code\":\"await element(by.text('Assertions')).tap();\"},{\"step\":\"Verify there is a blue color text element\",\"code\":\"throw new Error(\\\"No blue colored text element found in the current view hierarchy or snapshot image.\\\");\",\"result\":\"Caught an error while evaluating \\\"Verify there is a blue color text element\\\", tried with generated code: \\\"throw new Error(\\\"No blue colored text element found in the current view hierarchy or snapshot image.\\\");\\\". Validate the code against the APIs and hierarchy and continue with a different approach. If can't, return a code that throws a descriptive error.\"}],\"viewHierarchyHash\":\"c9baf8fa77bc24c637655565c898ea17\"}": "throw new Error(\"No blue colored text element found in the current view hierarchy or snapshot image.\");",
"{\"step\":\"Verify the image element is under the smiling face emoji element\",\"previous\":[{\"step\":\"Restart the React Native state\",\"code\":\"await device.reloadReactNative();\"},{\"step\":\"Navigate to the Assertions screen\",\"code\":\"await element(by.text('Assertions')).tap();\"},{\"step\":\"Verify there is a toggle element under the text element \\\"I contain some text\\\"\",\"code\":\"await expect(element(by.text('I contain some text'))).toExist();\\nawait expect(element(by.id('toggle'))).toExist();\"}],\"viewHierarchyHash\":\"c9baf8fa77bc24c637655565c898ea17\"}": "await expect(element(by.id('smile-emoji'))).toExist();\nawait expect(element(by.id('example-image'))).toExist();\n\nconst emojiAttrs = await element(by.id('smile-emoji')).getAttributes();\nconst imageAttrs = await element(by.id('example-image')).getAttributes();\n\njestExpect(emojiAttrs.y).toBeLessThan(imageAttrs.y);",
"{\"step\":\"Verify the image element is under the smiling face emoji element\",\"previous\":[{\"step\":\"Restart the React Native state\",\"code\":\"await device.reloadReactNative();\"},{\"step\":\"Navigate to the Assertions screen\",\"code\":\"await element(by.text('Assertions')).tap();\"},{\"step\":\"Verify there is a toggle element under the text element \\\"I contain some text\\\"\",\"code\":\"await expect(element(by.text('I contain some text'))).toExist();\\nawait expect(element(by.id('toggle'))).toExist();\"},{\"step\":\"Verify the image element is under the smiling face emoji element\",\"code\":\"await expect(element(by.id('smile-emoji'))).toExist();\\nawait expect(element(by.id('example-image'))).toExist();\\n\\nconst emojiAttrs = await element(by.id('smile-emoji')).getAttributes();\\nconst imageAttrs = await element(by.id('example-image')).getAttributes();\\n\\njestExpect(emojiAttrs.y).toBeLessThan(imageAttrs.y);\",\"result\":\"Caught an error while evaluating \\\"Verify the image element is under the smiling face emoji element\\\", tried with generated code: \\\"await expect(element(by.id('smile-emoji'))).toExist();\\nawait expect(element(by.id('example-image'))).toExist();\\n\\nconst emojiAttrs = await element(by.id('smile-emoji')).getAttributes();\\nconst imageAttrs = await element(by.id('example-image')).getAttributes();\\n\\njestExpect(emojiAttrs.y).toBeLessThan(imageAttrs.y);\\\". Validate the code against the APIs and hierarchy and continue with a different approach. If can't, return a code that throws a descriptive error.\"}],\"viewHierarchyHash\":\"c9baf8fa77bc24c637655565c898ea17\"}": "await expect(element(by.id('smile-emoji'))).toExist();\nawait expect(element(by.id('example-image'))).toExist();\n\nconst emojiAttrs = await element(by.id('smile-emoji')).getAttributes();\nconst imageAttrs = await element(by.id('example-image')).getAttributes();\n\njestExpect(emojiAttrs.y).toBeLessThan(imageAttrs.y);"
}
@@ -1,24 +0,0 @@
describe.forCopilot('Copilot Sanity', () => {
beforeEach(async () => {
await copilot.perform(
'Restart the React Native state',
'Navigate to the Sanity screen'
);
});
it('should have welcome screen', async () => {
await copilot.perform(
'Welcome text is displayed',
'Say Hello button is visible to the user',
'Can see a Say World button'
);
});
it('should show hello screen after tap', async () => {
await copilot.perform('Tap on Say Hello button', '"Hello!!!" text is visible');
});
it('should show world screen after tap', async () => {
await copilot.perform('Tap on Say World button', '"World!!!" text is displayed');
});
});
@@ -1,59 +0,0 @@
const { default: jestExpect } = require('expect');
describe.skipIfNewArchOnIOS('DatePicker', () => {
describe.forCopilot('Copilot', () => {
beforeEach(async () => {
await copilot.perform(
'Restart the React Native state',
'Navigate to the DatePicker screen'
);
});
describe('DatePicker Tests', () => {
// Note: when writing "Date (UTC):" instead of "Date (UTC): " copilot failed the test
it('correct date and time', async () => {
await copilot.perform(
'Verify there is element with the text "Date (UTC): "',
'Verify the element value of current date UTC July 1st 2023',
'Verify there is element with the text "Time (UTC): "',
'Verify there is element with the text "Time Local: "',
'Verify "Time Local: " value is 7:30 pm'
);
});
it('compact date picker', async () => {
await copilot.perform(
'Verify there is an element with the text "Compact Date Picker"',
'Verify there is an element with today`s date at the bottom of the screen',
'Set the date picker to September 9th, 2023'
);
});
it('inline date picker', async () => {
await copilot.perform(
'Verify there is an element with the text "Compact Date Picker"',
'Tap the element with the text "Compact Date Picker"',
'Verify there is an element with the text "Inline Date Picker"',
'Verify there is an element with today`s date at the bottom of the screen',
'Set the date picker to September 9th, 2023'
);
});
it('switch to spinner date picker', async () => {
await copilot.perform(
'Verify there is an element with the text "Compact Date Picker"',
'Tap the element with the text "Compact Date Picker"',
'Verify there is an element with the text "Inline Date Picker"',
'Tap the element with the text "Inline Date Picker"',
'Verify that there is slider element at the bottom of the screen',
'Set the date picker to September 9th, 2023'
);
await jestExpect(async () =>
await copilot.perform('Set the date picker`s first column to 10th, so the date will be September 10th, 2023')
).rejects.toThrowError();
});
});
});
});
@@ -0,0 +1,24 @@
describe.forPilot('Pilot Sanity', () => {
beforeEach(async () => {
await pilot.perform(
'Restart the React Native state',
'Navigate to the Sanity screen'
);
});
it('should have welcome screen', async () => {
await pilot.perform(
'Welcome text is displayed',
'Say Hello button is visible to the user',
'Can see a Say World button'
);
});
it('should show hello screen after tap', async () => {
await pilot.autopilot('tap on the Say Hello button in the sanity screen and expect to see "Hello!!!" text displayed');
});
it('should show world screen after tap with Copilot (deprecated API)', async () => {
await copilot.autopilot('tap on the Say World button in the sanity screen and expect to see "World!!!" text displayed');
});
});
@@ -1,36 +1,36 @@
const jestExpect = require('expect').default;
describe.forCopilot('Copilot Actions', () => {
describe.forPilot('Pilot Actions', () => {
beforeEach(async () => {
await copilot.perform(
await pilot.perform(
'Restart the React Native environment',
'Go to the Actions screen'
);
});
it('should tap on an element', async () => {
await copilot.perform(
await pilot.perform(
'Press the "Tap Me" button',
'The text "Tap Working!!!" is shown on the screen'
);
});
it('should long press on an element', async () => {
await copilot.perform(
await pilot.perform(
'Perform a long press on the "Tap Me" button',
'The message "Long Press Working!!!" is displayed'
);
});
it('should long press with duration on an element', async () => {
await copilot.perform(
await pilot.perform(
'Hold the "Long Press Me 1.5s" button for 1.5 seconds',
'Can see "Long Press With Duration Working!!!" on the screen'
);
});
it('should long press with point', async () => {
await copilot.perform(
await pilot.perform(
'Long press the top-most left-most corner of the "Long Press on Top Left" button',
'The text "Long Press on Top Left Working!!!" appears'
);
@@ -38,19 +38,19 @@ describe.forCopilot('Copilot Actions', () => {
it('should not succeed in long pressing with point outside the target area', async () => {
await jestExpect(async () =>
await copilot.perform('Attempt a long press on the "Long Press on Top Left" button outside its bounds')
await pilot.perform('Attempt a long press on the "Long Press on Top Left" button outside its bounds')
).rejects.toThrowError();
});
it('should type in an element', async () => {
await copilot.perform(
await pilot.perform(
'Enter "Type Working!!!" into the text input field',
'The typed text is visible on the screen'
);
});
it('should press the backspace key on an element', async () => {
await copilot.perform(
await pilot.perform(
'Input "test" in the text field',
'Hit the backspace key in the text input',
'The typed text is shown in the input field'
@@ -58,35 +58,35 @@ describe.forCopilot('Copilot Actions', () => {
});
it('should press the return key on an element', async () => {
await copilot.perform(
await pilot.perform(
'Tap the return key on the keyboard for the text input',
'The message "Return Working!!!" is visible to the user'
);
});
it('should clear text in an element', async () => {
await copilot.perform(
await pilot.perform(
'Remove all text from the text input that already has text in it',
'The text "Clear Working!!!" appears on the screen'
);
});
it('should replace text in an element', async () => {
await copilot.perform(
await pilot.perform(
'Substitute the existing text with "replaced_text" in the test_id="UniqueId006" field',
'The message "Replace Working!!!" is shown'
);
});
it('should swipe down until pull to reload is triggered', async () => {
await copilot.perform(
await pilot.perform(
'Swipe fast the scrollable area ScrollView799 downwards to activate the pull-to-reload',
'The text "PullToReload Working!!!" becomes visible'
);
});
it('should swipe vertically', async () => {
await copilot.perform(
await pilot.perform(
'The element with text "Text1" can be seen',
'Swipe the view "ScrollView161" upwards',
'The Text1 element is no longer in view',
@@ -95,7 +95,7 @@ describe.forCopilot('Copilot Actions', () => {
});
it('should swipe horizontally', async () => {
await copilot.perform(
await pilot.perform(
'The "HText1" element is present',
// To avoid confusion: left swipe scrolls right
'Left-swipe the horizontal scrollable area "ScrollViewH"',
@@ -106,7 +106,7 @@ describe.forCopilot('Copilot Actions', () => {
});
it('should adjust slider and assert its value', async () => {
await copilot.perform(
await pilot.perform(
'The slider is set to 25%',
'Move the slider to the 75% position',
'The slider value is approximately 75%, give or take 10%'
@@ -114,7 +114,7 @@ describe.forCopilot('Copilot Actions', () => {
});
it('should expect text fields to be focused after tap but not before', async () => {
await copilot.perform(
await pilot.perform(
'The text field UniqueId005 (call it "the first") does not have focus',
'Text input UniqueId006 (call it "the second") is not currently focused',
'Tap to focus on the first text field',
@@ -128,13 +128,13 @@ describe.forCopilot('Copilot Actions', () => {
it('should assert on invalid intent', async () => {
await jestExpect(async () =>
await copilot.perform('Tap the "FOOBAR" button')
await pilot.perform('Tap the "FOOBAR" button')
).rejects.toThrowError();
});
it('should assert on ambiguous intent', async () => {
await jestExpect(async () =>
await copilot.perform('Do magic to the element')
await pilot.perform('Do magic to the element')
).rejects.toThrowError();
});
});
@@ -1,13 +1,13 @@
describe.forCopilot('Shape Match Game Screen', () => {
describe.forPilot('Shape Match Game Screen', () => {
beforeEach(async () => {
await copilot.perform(
await pilot.perform(
'Reset react native state',
'Enter the "Shape Matching" game screen'
);
});
it('should play the Shape Matching game', async () => {
await copilot.perform(
await pilot.perform(
'Game has started with score 0',
'Drag the blue square into the middle of its hole',
@@ -1,6 +1,6 @@
describe.forCopilot('WebView Interactions', () => {
describe.forPilot('WebView Interactions', () => {
beforeEach(async () => {
await copilot.perform(
await pilot.perform(
'Restart the React Native state',
'Navigate to the WebView screen'
);
@@ -8,7 +8,7 @@ describe.forCopilot('WebView Interactions', () => {
describe('Single WebView Tests', () => {
it('should interact with elements in a single WebView', async () => {
await copilot.perform(
await pilot.perform(
'Find an element with ID "pageHeadline" in the WebView',
'Verify that the text of this element is "First Webview"',
'Locate an input field with ID "fname"',
@@ -20,7 +20,7 @@ describe.forCopilot('WebView Interactions', () => {
});
it('should scroll and interact with elements', async () => {
await copilot.perform(
await pilot.perform(
'Scroll to "bottomParagraph" in the WebView',
'Verify that the text of this element is "This is a bottom paragraph with class."',
);
@@ -29,13 +29,13 @@ describe.forCopilot('WebView Interactions', () => {
describe('Multiple WebViews Tests', () => {
beforeEach(async () => {
await copilot.perform(
await pilot.perform(
'Enable the second WebView'
);
});
it('should interact with elements in multiple WebViews', async () => {
await copilot.perform(
await pilot.perform(
'In the second WebView, verify the headline has the message "This is a dummy webview."',
'Hide the second WebView',
'Show the 3rd WebView',
@@ -1,15 +1,15 @@
const {expectToThrow} = require("../utils/custom-expects");
describe.forCopilot(':ios: iOS Permission Dialogs', () => {
describe.forPilot(':ios: iOS Permission Dialogs', () => {
beforeEach(async () => {
await copilot.perform(
await pilot.perform(
'Remove the app and start a fresh instance',
'Navigate to the System Dialogs screen'
);
});
it('should handle permission request dialogs', async () => {
await copilot.perform(
await pilot.perform(
'Check that the initial permission status is "denied"',
'Tap the button to request permission',
'A system dialog appears asking for permission',
@@ -19,7 +19,7 @@ describe.forCopilot(':ios: iOS Permission Dialogs', () => {
});
it('should handle permission request dialogs with different button types', async () => {
await copilot.perform(
await pilot.perform(
'Tap on request permission',
'Deny the permission request',
'Verify that the permission status now says "blocked"'
@@ -28,7 +28,7 @@ describe.forCopilot(':ios: iOS Permission Dialogs', () => {
it('should handle non-existent system elements', async () => {
await expectToThrow(async () => {
await copilot.perform('Interact with the system element with the text "Press Me"',);
await pilot.perform('Interact with the system element with the text "Press Me"',);
});
});
});
@@ -1,15 +1,15 @@
const {expectToThrow} = require("../utils/custom-expects");
describe.forCopilot('WaitFor Functionality', () => {
describe.forPilot('WaitFor Functionality', () => {
beforeEach(async () => {
await copilot.perform(
await pilot.perform(
'Restart the React Native environment',
'Navigate to the WaitFor screen'
);
});
it('should wait for elements to appear and disappear', async () => {
await copilot.perform(
await pilot.perform(
'Verify that an element with ID "changeExistenceByToggle" is not present',
'Tap the "Go" button',
'Wait for the element to appear',
@@ -21,7 +21,7 @@ describe.forCopilot('WaitFor Functionality', () => {
});
it('should wait for elements to become focused and unfocused', async () => {
await copilot.perform(
await pilot.perform(
'Check that an element with ID "changeFocusByToggle" is not focused',
'Tap the "Go" button',
'Wait for the element to become focused',
@@ -33,7 +33,7 @@ describe.forCopilot('WaitFor Functionality', () => {
});
it('should find elements by scrolling', async () => {
await copilot.perform(
await pilot.perform(
'Verify that an element with text "Text5" is not visible',
'Tap the "Go" button',
'Scroll down in the ScrollView until "Text5" becomes visible',
@@ -43,7 +43,7 @@ describe.forCopilot('WaitFor Functionality', () => {
it('should handle timeouts for non-appearing elements', async () => {
await expectToThrow(async () => {
await copilot.perform(
await pilot.perform(
'Try to wait for an element with ID "neverAppearingText" to appear, with timeout of 2 seconds'
);
});
@@ -1,8 +1,8 @@
const jestExpect = require('expect').default;
describe.forCopilot('Assertions', () => {
describe.forPilot('Assertions', () => {
beforeEach(async () => {
await copilot.perform(
await pilot.perform(
'Restart the React Native state',
'Navigate to the Assertions screen'
);
@@ -10,77 +10,77 @@ describe.forCopilot('Assertions', () => {
describe('Assertion Tests', () => {
it('should assert an element is visible (by text)', async () => {
await copilot.perform(
await pilot.perform(
'Verify there is an element with the text "i contain some text"'
);
});
it('should not assert an element is not visible (by text)', async () => {
await jestExpect(async () =>
await copilot.perform('Verify there is element with the text "I am full of text" in the screen')
await pilot.perform('Verify there is element with the text "I am full of text" in the screen')
).rejects.toThrowError();
});
it('should assert an element is visible (by id)', async () => {
await copilot.perform(
await pilot.perform(
'Verify there is an element with ID "subtext-root"'
);
});
it('should not assert an element that does not exist (by id)', async () => {
await jestExpect(async () =>
await copilot.perform('Find an element with ID "RandomJunk959" in the screen')
await pilot.perform('Find an element with ID "RandomJunk959" in the screen')
).rejects.toThrowError();
});
it('should assert an element has (accessibility) label', async () => {
await copilot.perform(
await pilot.perform(
'Verify there is an element with the accessibility label "I contain some text"'
);
});
it('should not assert an element that does not exist (by label)', async () => {
await jestExpect(async () =>
await copilot.perform('Find an element with label "I exist in the screen" in the screen')
await pilot.perform('Find an element with label "I exist in the screen" in the screen')
).rejects.toThrowError();
});
it('assert toggle exist by element type', async () => {
await copilot.perform(
await pilot.perform(
'Verify that the toggle type element is exist in the screen'
);
});
// Note: This test is skipped because currently we don't know what are the exact expectations.
// Should we expect the copilot to understand the similarity between toggle and checkbox or not?
// Should we expect the pilot to understand the similarity between toggle and checkbox or not?
// Currently, if it is not skipped, it passes.
it.skip('shouldn`t assert an element that does not exist (by type)', async () => {
await jestExpect(async () =>
await copilot.perform('Find a Check-box type element in the screen')
await pilot.perform('Find a Check-box type element in the screen')
).rejects.toThrowError();
});
it('shouldn`t assert an element that does not exist (by type, text-field)', async () => {
await jestExpect(async () =>
await copilot.perform('Find a text-field element in the screen')
await pilot.perform('Find a text-field element in the screen')
).rejects.toThrowError();
});
it('assert toggle-switch widget false', async () => {
await copilot.perform(
await pilot.perform(
'Verify that the toggle has false value'
);
});
it('assert toggle-switch widget true ', async () => {
await copilot.perform(
await pilot.perform(
'Tap the toggle',
'Verify that the toggle has true value'
);
});
it('assert details in image', async () => {
await copilot.perform(
await pilot.perform(
'Verify there is an image element in the screen',
'Verify there is a banana in the image element',
'Verify there isn`t an apple in the image element',
@@ -89,7 +89,7 @@ describe.forCopilot('Assertions', () => {
});
it('assert element`s color', async () => {
await copilot.perform(
await pilot.perform(
'Verify there is a green text element'
);
});
@@ -97,14 +97,14 @@ describe.forCopilot('Assertions', () => {
// Note: The test failed: "color information is not available in the view hierarchy"
// Same test with green instead of blue - passed.
it.skip('assert element`s color with text contains another color', async () => {
await copilot.perform(
await pilot.perform(
'Verify there is a blue color text element'
);
});
describe('order assertions', () => {
it('assert elements are in specific order', async () => {
await copilot.perform(
await pilot.perform(
'Verify there is an element with the text "subtext"',
'Verify that under the text element with the text "subtext" there is an image element',
'Verify that under the image element there is a green text element',
@@ -114,17 +114,17 @@ describe.forCopilot('Assertions', () => {
it('raise error when elements are not in specific order', async () => {
await jestExpect(async () =>
await copilot.perform('Verify the toggle element is above the text element')
await pilot.perform('Verify the toggle element is above the text element')
).rejects.toThrowError();
await jestExpect(async () =>
await copilot.perform('Verify that under the image element in the screen there is text element with the text "subtext"')
await pilot.perform('Verify that under the image element in the screen there is text element with the text "subtext"')
).rejects.toThrowError();
});
// Note: The `not directly` tests are skipped because they are flaky, and not always working as expected.
it.skip('assert elements are in specific order - not directly', async () => {
await copilot.perform(
await pilot.perform(
'Verify there is a toggle element under the text element "I contain some text"',
'Verify the image element is under the smiling face emoji element'
);
@@ -132,16 +132,16 @@ describe.forCopilot('Assertions', () => {
it.skip('raise error when elements are not in specific order - not directly', async () => {
await jestExpect(async () =>
await copilot.perform('Verify the the image element is above smiling face emoji element')
await pilot.perform('Verify the the image element is above smiling face emoji element')
).rejects.toThrowError();
await jestExpect(async () =>
await copilot.perform('Verify there is the text element "I contain some text" under the toggle element')
await pilot.perform('Verify there is the text element "I contain some text" under the toggle element')
).rejects.toThrowError();
});
it('assert element`s position', async () => {
await copilot.perform(
await pilot.perform(
'Verify there is a smiling emoji in the top left corner of the screen',
'Verify there is a party emoji in the bottom right corner of the screen'
);
@@ -149,15 +149,15 @@ describe.forCopilot('Assertions', () => {
it('raise error when elements are not positioned right', async () => {
await jestExpect(async () =>
await copilot.perform('Verify there is a smiling face emoji in the center of the screen')
await pilot.perform('Verify there is a smiling face emoji in the center of the screen')
).rejects.toThrowError();
await jestExpect(async () =>
await copilot.perform('Verify there is a party emoji in the center of the screen')
await pilot.perform('Verify there is a party emoji in the center of the screen')
).rejects.toThrowError();
await jestExpect(async () =>
await copilot.perform('Verify there is a smiling face emoji in the bottom right corner of the screen')
await pilot.perform('Verify there is a smiling face emoji in the bottom right corner of the screen')
).rejects.toThrowError();
});
});
@@ -3,18 +3,18 @@ const DUMMY_COORDINATE1_LATITUDE = '-80.125';
const DUMMY_COORDINATE2_LONGITUDE = '-80.125';
const DUMMY_COORDINATE2_LATITUDE = '66.5';
describe.forCopilot('Location', () => {
describe.forPilot('Location', () => {
beforeEach(async () => {
await copilot.perform(
await pilot.perform(
'Restart the React Native state',
'Navigate to the Location screen'
);
});
describe('Location Tests', () => {
it('should show error when permission defined as `never`', async () => {
await copilot.perform(
await pilot.perform(
'Launch the app with location permission denied',
'Navigate to the Location screen',
'Verify there is an element with the text "Get location"',
'Tap the get location element',
'Verify there is an element with the text "User denied access to location services."'
@@ -22,8 +22,9 @@ describe.forCopilot('Location', () => {
});
it('should show location when permission is `always`', async () => {
await copilot.perform(
await pilot.perform(
'Launch the app with location permission always',
'Navigate to the Location screen',
'Verify there is an element with the text "Get location"',
'Tap the get location element',
`Set the device location (${DUMMY_COORDINATE1_LATITUDE}, ${DUMMY_COORDINATE1_LONGITUDE})`,
@@ -33,8 +34,9 @@ describe.forCopilot('Location', () => {
});
it('should show location when permission is `inuse`', async () => {
await copilot.perform(
await pilot.perform(
'Launch the app with location permission just once',
'Navigate to the Location screen',
'Verify there is an element with the text "Get location"',
'Tap the get location element',
`Set the device location to (${DUMMY_COORDINATE1_LATITUDE}, ${DUMMY_COORDINATE1_LONGITUDE})`,
@@ -44,8 +46,9 @@ describe.forCopilot('Location', () => {
});
it('should set location multiple times', async () => {
await copilot.perform(
await pilot.perform(
'Launch the app with location permission just once',
'Navigate to the Location screen',
'Verify there is an element with the text "Get location"',
'Tap the get location element',
`Set the device location to (${DUMMY_COORDINATE1_LATITUDE}, ${DUMMY_COORDINATE1_LONGITUDE})`,
@@ -0,0 +1,57 @@
const { default: jestExpect } = require('expect');
describe.forPilot('DatePicker', () => {
beforeEach(async () => {
await pilot.perform(
'Restart the React Native state',
'Navigate to the DatePicker screen'
);
});
describe('DatePicker Tests', () => {
// Note: when writing "Date (UTC):" instead of "Date (UTC): " pilot failed the test
it('correct date and time', async () => {
await pilot.perform(
'Verify there is element with the text "Date (UTC): "',
'Verify the element value of current date UTC July 1st 2023',
'Verify there is element with the text "Time (UTC): "',
'Verify there is element with the text "Time Local: "',
'Verify "Time Local: " value is 7:30 pm'
);
});
it('compact date picker', async () => {
await pilot.perform(
'Verify there is an element with the text "Compact Date Picker"',
'Verify there is an element with today`s date at the bottom of the screen',
'Set the date picker to September 9th, 2023'
);
});
it('inline date picker', async () => {
await pilot.perform(
'Verify there is an element with the text "Compact Date Picker"',
'Tap the element with the text "Compact Date Picker"',
'Verify there is an element with the text "Inline Date Picker"',
'Verify there is an element with today`s date at the bottom of the screen',
'Set the date picker to September 9th, 2023'
);
});
it('switch to spinner date picker', async () => {
await pilot.perform(
'Verify there is an element with the text "Compact Date Picker"',
'Tap the element with the text "Compact Date Picker"',
'Verify there is an element with the text "Inline Date Picker"',
'Tap the element with the text "Inline Date Picker"',
'Verify that there is slider element at the bottom of the screen',
'Set the date picker to September 9th, 2023'
);
await jestExpect(async () =>
await pilot.perform('Set the date picker`s first column to 10th, so the date will be September 10th, 2023')
).rejects.toThrowError();
});
});
});
@@ -1,9 +1,9 @@
const { default: jestExpect } = require('expect');
describe.forCopilot('Visibility', () => {
describe.forPilot('Visibility', () => {
describe('Visibility Expectation', () => {
beforeEach(async () => {
await copilot.perform(
await pilot.perform(
'Restart the React Native state',
'Navigate to the Visibility Expectation screen'
);
@@ -11,7 +11,7 @@ describe.forCopilot('Visibility', () => {
describe('before move element', () => {
it('should be truthy when at least 50% visibility is required', async () => {
await copilot.perform(
await pilot.perform(
'Verify there is a text element with the text "Element should be half-visible"',
'Verify the purple rectangle below the text that is exactly 50% visible',
);
@@ -19,25 +19,25 @@ describe.forCopilot('Visibility', () => {
it('should be falsy when at least 51% visibility is required', async () => {
await jestExpect(async () =>
await copilot.perform('Verify the purple rectangle is 51% visible')
await pilot.perform('Verify the purple rectangle is 51% visible')
).rejects.toThrowError();
});
});
describe('after move element', () => {
beforeEach(async () => {
await copilot.perform('Tap the button with the text "Move That Element"');
await pilot.perform('Tap the button with the text "Move That Element"');
});
it('should be truthy when at least 25% visibility is required', async () => {
await copilot.perform(
await pilot.perform(
'Verify the purple rectangle is exactly 25% visible',
);
});
it('should be falsy when at least 26% visibility is required', async () => {
await jestExpect(async () =>
await copilot.perform('Verify the purple rectangle is exactly 26% visible')
await pilot.perform('Verify the purple rectangle is exactly 26% visible')
).rejects.toThrowError();
});
});
@@ -1,7 +1,10 @@
const axios = require('axios');
const fs = require('fs').promises;
class PromptHandler {
/**
* @type {Detox.PromptHandler}
*/
class WixPromptHandler {
async uploadImage(imagePath) {
const image = await fs.readFile(imagePath);
@@ -55,4 +58,4 @@ class PromptHandler {
}
}
module.exports = PromptHandler;
module.exports = WixPromptHandler;
+5 -5
View File
@@ -1,5 +1,5 @@
const axios = require('axios');
const PromptHandler = require("./PromptHandler");
const WixPromptHandler = require("./WixPromptHandler");
const { isRNNewArch } = require('../../../src/utils/rn-consts/rn-consts');
describe.skipIfCI = (title, fn) => {
@@ -15,17 +15,17 @@ describe.skipIfNewArchOnIOS = (title, fn) => {
return describe(title, fn);
};
describe.forCopilot = (description, fn) => {
return describe.skipIfCI(':ios: Copilot', () => {
describe.forPilot = (description, fn) => {
return describe.skipIfCI(':ios: Pilot', () => {
describe(description, () => {
beforeAll(async () => {
if (!await _checkVpnStatus()) {
console.warn('Cannot access the LLM service without Wix BO environment. Relying on cached responses only.');
}
try {
await copilot.init(new PromptHandler());
await pilot.init(new WixPromptHandler());
} catch (error) {
if (error.message.includes('Copilot has already been initialized')) {
if (error.message.includes('Pilot has already been initialized')) {
// Ignore already initialized error
} else {
throw error;
@@ -717,7 +717,10 @@
"-DFOLLY_CFG_NO_COROUTINES=1",
"-DFOLLY_HAVE_CLOCK_GETTIME=1",
);
OTHER_LDFLAGS = "$(inherited) ";
OTHER_LDFLAGS = (
"$(inherited)",
" ",
);
REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native";
SDKROOT = iphoneos;
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) DEBUG";
@@ -786,7 +789,10 @@
"-DFOLLY_CFG_NO_COROUTINES=1",
"-DFOLLY_HAVE_CLOCK_GETTIME=1",
);
OTHER_LDFLAGS = "$(inherited) ";
OTHER_LDFLAGS = (
"$(inherited)",
" ",
);
REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native";
SDKROOT = iphoneos;
USE_HERMES = true;