Skip to content

Commit

Permalink
Select date on IOS UIDatePicker with actionForSetDate (wix#1148)
Browse files Browse the repository at this point in the history
* Select date on IOS UIDatePicker with actionForSetDate

* Remove IOSOnly suffix from method name

* Remove extraneous line

* Add documentation for setDatePickerDate
  • Loading branch information
matthewrfindley authored and LeoNatan committed Feb 9, 2019
1 parent bc69538 commit 6a1d700
Show file tree
Hide file tree
Showing 12 changed files with 140 additions and 29 deletions.
Empty file modified detox-cli/cli.js
100644 → 100755
Empty file.
6 changes: 6 additions & 0 deletions detox/ios/Detox.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
394D589C1E50B59400556DB9 /* NSBundle+TestsFix.m in Sources */ = {isa = PBXBuildFile; fileRef = 394D589B1E50B59400556DB9 /* NSBundle+TestsFix.m */; };
397EC9B51E7EDE0B00D5F2BB /* EarlGreyStatistics.h in Headers */ = {isa = PBXBuildFile; fileRef = 397EC9B31E7EDE0B00D5F2BB /* EarlGreyStatistics.h */; };
397EC9B61E7EDE0B00D5F2BB /* EarlGreyStatistics.m in Sources */ = {isa = PBXBuildFile; fileRef = 397EC9B41E7EDE0B00D5F2BB /* EarlGreyStatistics.m */; };
398260E8220CC9530061E83E /* GREYActions+Detox.m in Sources */ = {isa = PBXBuildFile; fileRef = 398260D4220CA56D0061E83E /* GREYActions+Detox.m */; };
399BF36821933F0C00F96D50 /* ExternalLogging.h in Headers */ = {isa = PBXBuildFile; fileRef = 399BF36621933F0C00F96D50 /* ExternalLogging.h */; };
399BF36921933F0C00F96D50 /* ExternalLogging.m in Sources */ = {isa = PBXBuildFile; fileRef = 399BF36721933F0C00F96D50 /* ExternalLogging.m */; };
39A34C711E30F10D00BEBB59 /* DetoxAppDelegateProxy.h in Headers */ = {isa = PBXBuildFile; fileRef = 39A34C6F1E30F10D00BEBB59 /* DetoxAppDelegateProxy.h */; settings = {ATTRIBUTES = (Private, ); }; };
Expand Down Expand Up @@ -230,6 +231,8 @@
3975C78220272ED500C59ED8 /* src */ = {isa = PBXFileReference; lastKnownFileType = folder; name = src; path = ../src; sourceTree = "<group>"; };
397EC9B31E7EDE0B00D5F2BB /* EarlGreyStatistics.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EarlGreyStatistics.h; sourceTree = "<group>"; };
397EC9B41E7EDE0B00D5F2BB /* EarlGreyStatistics.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = EarlGreyStatistics.m; sourceTree = "<group>"; };
398260CE220CA5380061E83E /* GREYActions+Detox.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "GREYActions+Detox.h"; sourceTree = "<group>"; };
398260D4220CA56D0061E83E /* GREYActions+Detox.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "GREYActions+Detox.m"; sourceTree = "<group>"; };
399BF36621933F0C00F96D50 /* ExternalLogging.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ExternalLogging.h; sourceTree = "<group>"; };
399BF36721933F0C00F96D50 /* ExternalLogging.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ExternalLogging.m; sourceTree = "<group>"; };
39A34C6F1E30F10D00BEBB59 /* DetoxAppDelegateProxy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DetoxAppDelegateProxy.h; sourceTree = "<group>"; };
Expand Down Expand Up @@ -395,6 +398,8 @@
391FA5E81E7FD96D0056F82F /* GREYIdlingResourcePrettyPrint.m */,
46A6A6341EF696B600E3AA79 /* GREYConfiguration+Detox.h */,
46A6A63B1EF697BB00E3AA79 /* GREYConfiguration+Detox.m */,
398260CE220CA5380061E83E /* GREYActions+Detox.h */,
398260D4220CA56D0061E83E /* GREYActions+Detox.m */,
);
name = EarlGreyExtensions;
sourceTree = "<group>";
Expand Down Expand Up @@ -700,6 +705,7 @@
39CEFCDB1E34E91B00A09124 /* DetoxUserNotificationDispatcher.swift in Sources */,
394767C21DBF98A700D72256 /* GREYMatchers+Detox.m in Sources */,
394767AF1DBF987E00D72256 /* DetoxManager.m in Sources */,
398260E8220CC9530061E83E /* GREYActions+Detox.m in Sources */,
39FFD9471FD730A600C97030 /* DetoxCrashHandler.mm in Sources */,
46A6A63D1EF697BB00E3AA79 /* GREYConfiguration+Detox.m in Sources */,
);
Expand Down
2 changes: 2 additions & 0 deletions detox/ios/Detox/DTXMethodInvocation.m
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@

#import "DTXMethodInvocation.h"
@import EarlGrey;
#import <EarlGrey/GreyActions.h>
#import "GREYActions+Detox.h"

@implementation DTXMethodInvocation

Expand Down
16 changes: 16 additions & 0 deletions detox/ios/Detox/GREYActions+Detox.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
//
// GREYActions+Detox.h
// Detox
//
// Created by Matt Findley on 2/7/19.
// Copyright © 2019 Wix. All rights reserved.
//

@import Foundation;
#import <EarlGrey/EarlGrey.h>

@interface GREYActions (Detox)

+ (id<GREYAction>)detoxSetDatePickerDate:(NSString *)dateString withFormat:(NSString *)dateFormat;

@end
23 changes: 23 additions & 0 deletions detox/ios/Detox/GREYActions+Detox.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
//
// GREYActions+Detox.m
// Detox
//
// Created by Matt Findley on 2/7/19.
// Copyright © 2019 Wix. All rights reserved.
//

#import <Foundation/Foundation.h>
#import "GREYActions+Detox.h"
#import <EarlGrey/GREYActions.h>

@implementation GREYActions (Detox)

+ (id<GREYAction>)detoxSetDatePickerDate:(NSString *)dateString withFormat:(NSString *)dateFormat
{
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
formatter.dateFormat = dateFormat;

NSDate *date = [formatter dateFromString:dateString];
return [GREYActions actionForSetDate:date];
}
@end
31 changes: 31 additions & 0 deletions detox/src/ios/earlgreyapi/GREYActions+Detox.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/**
This code is generated.
For more information see generation/README.md.
*/



class GREYActions {
static detoxSetDatePickerDateWithFormat(dateString, dateFormat) {
if (typeof dateString !== "string") throw new Error("dateString should be a string, but got " + (dateString + (" (" + (typeof dateString + ")"))));
if (typeof dateFormat !== "string") throw new Error("dateFormat should be a string, but got " + (dateFormat + (" (" + (typeof dateFormat + ")"))));
return {
target: {
type: "Class",
value: "GREYActions"
},
method: "detoxSetDatePickerDate:withFormat:",
args: [{
type: "NSString",
value: dateString
}, {
type: "NSString",
value: dateFormat
}]
};
}

}

module.exports = GREYActions;
13 changes: 12 additions & 1 deletion detox/src/ios/expect.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ const GreyActions = require('./earlgreyapi/GREYActions');
const GreyInteraction = require('./earlgreyapi/GREYInteraction');
const GreyCondition = require('./earlgreyapi/GREYCondition');
const GreyConditionDetox = require('./earlgreyapi/GREYConditionDetox');
const GreyActionsDetox = require('./earlgreyapi/GREYActions+Detox');

let invocationManager;

Expand Down Expand Up @@ -184,7 +185,14 @@ class SwipeAction extends Action {
class ScrollColumnToValue extends Action {
constructor(column,value) {
super();
this._call = invoke.callDirectly(GreyActions.actionForSetPickerColumnToValue(column,value))
this._call = invoke.callDirectly(GreyActions.actionForSetPickerColumnToValue(column, value));
}
}

class SetDatePickerDate extends Action {
constructor(dateString, dateFormat) {
super();
this._call = invoke.callDirectly(GreyActionsDetox.detoxSetDatePickerDateWithFormat(dateString, dateFormat));
}
}

Expand Down Expand Up @@ -330,6 +338,9 @@ class Element {
async setColumnToValue(column,value) {
return await new ActionInteraction(this, new ScrollColumnToValue(column, value)).execute();
}
async setDatePickerDate(dateString, dateFormat) {
return await new ActionInteraction(this, new SetDatePickerDate(dateString, dateFormat)).execute();
}
}

class Expect { }
Expand Down
3 changes: 3 additions & 0 deletions detox/src/ios/expect.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ describe('expect', async () => {
it(`element by type`, async () => {
await e.expect(e.element(e.by.type('test'))).toBeVisible();
await e.element(e.by.type('UIPickerView')).setColumnToValue(1,"6");
await e.element(e.by.type('UIPickerView')).setDatePickerDate('2019-2-8T05:10:00-08:00', "yyyy-MM-dd'T'HH:mm:ssZZZZZ");
});

it(`element by traits`, async () => {
Expand Down Expand Up @@ -152,6 +153,8 @@ describe('expect', async () => {
await expectToThrow(() => e.element(e.by.id('ScrollView799')).swipe('down', 'NotFastNorSlow'));
await expectToThrow(() => e.element(e.by.id('ScrollView799')).swipe('down', 'NotFastNorSlow', 0.9));
await expectToThrow(() => e.element(e.by.id('ScrollView799')).atIndex('NaN'));
await expectToThrow(() => e.element(e.by.type('UIPickerView')).setDatePickerDate(0, 'mm'));
await expectToThrow(() => e.element(e.by.type('UIPickerView')).setDatePickerDate('something', 0));
});

it(`exportGlobals() should export api functions`, async () => {
Expand Down
11 changes: 8 additions & 3 deletions detox/test/e2e/17.datePicker.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,17 @@ describe(':ios: DatePicker', () => {
await device.reloadReactNative();
await element(by.text('DatePicker')).tap();
});

it('datePicker should trigger change handler correctly', async () => {
await element(by.type('UIPickerView')).setColumnToValue(1,"6");
await element(by.type('UIPickerView')).setColumnToValue(2,"34");
await expect(element(by.id("timeLabel"))).toHaveText('choosenTime is 6:34');
});

});

it('can select dates on a UIDatePicker', async () => {
await element(by.type('UIDatePicker')).setDatePickerDate('2019-02-06T05:10:00-08:00', "yyyy-MM-dd'T'HH:mm:ssZZZZZ");

await expect(element(by.id('dateTimeLabel'))).toHaveText('choosenDateTime is 2-6-2019 5:10');
});

});
48 changes: 25 additions & 23 deletions detox/test/src/Screens/DatePickerScreen.js
Original file line number Diff line number Diff line change
@@ -1,47 +1,49 @@
import React, { Component } from 'react';
import {
Text,
View,
StyleSheet,
DatePickerIOS
} from 'react-native';
import { Text, View, StyleSheet, DatePickerIOS } from 'react-native';

export default class DatePickerScreen extends Component {

constructor(props) {
super(props);
this.state = {
chosenDate: new Date()
}
};
this._setDate = this._setDate.bind(this);
}

_setDate(newDate) {
this.setState({
chosenDate :newDate
})
chosenDate: newDate
});
}

getTime() {
minutes = this.state.chosenDate.getMinutes()
hour = this.state.chosenDate.getHours()
if( hour > 12 ) {
minutes = this.state.chosenDate.getMinutes();
hour = this.state.chosenDate.getHours();

if (hour > 12) {
hour = hour - 12;
}
return `${hour}:${minutes}`
return `${hour}:${minutes}`;
}

getDateTime() {
year = this.state.chosenDate.getFullYear();
month = this.state.chosenDate.getMonth() + 1;
day = this.state.chosenDate.getDate();

return `${month}-${day}-${year} ${this.getTime()}`;
}

render() {
return (
<View style={{flex: 1, paddingTop: 20, justifyContent: 'center', alignItems: 'center'}}>
<Text style = {styles.dateText} testID='timeLabel'>
<Text style={styles.dateText} testID='timeLabel'>
{"choosenTime is " + this.getTime()}
</Text>
<DatePickerIOS
style = {styles.datePicker}
date = {this.state.chosenDate}
onDateChange={this._setDate}
/>
<Text style={styles.dateText} testID="dateTimeLabel">
{"choosenDateTime is " + this.getDateTime()}
</Text>
<DatePickerIOS style={styles.datePicker} date={this.state.chosenDate} onDateChange={this._setDate} />
</View>
);
}
Expand All @@ -51,9 +53,9 @@ const styles = StyleSheet.create({
datePicker: {
width:'100%',
height:200,
backgroundColor:'green',
backgroundColor:'green'
},
dateText: {
textAlign: 'center',
}
textAlign:'center'
}
});
13 changes: 12 additions & 1 deletion docs/APIRef.ActionsOnElement.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ Actions are functions that emulate user behavior. They are being performed on ma
- [`.swipe()`](#swipedirection-speed-percentage)
- [`.setColumnToValue()`](#setcolumntovaluecolumnvalue--ios-only) **iOS only**
- [`.pinchWithAngle()`](#pinchWithAngle--ios-only) **iOS only**
- [`.setDatePickerDate()`](#setdatepickerdate--ios-only) **iOS only**


### `tap()`
Expand Down Expand Up @@ -146,4 +147,14 @@ angle - value in radiant - default is 0<br>
```js
await expect(element(by.id('PinchableScrollView'))).toBeVisible();
await element(by.id('PinchableScrollView')).pinchWithAngle('outward', 'slow', 0);
```
```

### `setDatePickerDate(dateString, dateFormat)` iOS only

dateString - string representing a date in the supplied dateFormat<br>
dateFormat - format for the dateString supplied<br>

```js
await expect(element(by.type('UIDatePicker'))).toBeVisible();
await element(by.type('UIDatePicker)).setDatePickerDate('2019-02-06T05:10:00-08:00', "yyyy-MM-dd'T'HH:mm:ssZZZZZ");
```
3 changes: 2 additions & 1 deletion generation/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ const iosFiles = {
'../detox/ios/EarlGrey/EarlGrey/Synchronization/GREYCondition.h': '../detox/src/ios/earlgreyapi/GREYCondition.js',
'../detox/ios/Detox/GREYConfiguration+Detox.h': '../detox/src/ios/earlgreyapi/GREYConfigurationDetox.js',
'../detox/ios/EarlGrey/EarlGrey/Common/GREYConfiguration.h': '../detox/src/ios/earlgreyapi/GREYConfiguration.js',
'../detox/ios/EarlGrey/EarlGrey/Core/EarlGreyImpl.h': '../detox/src/ios/earlgreyapi/EarlGreyImpl.js'
'../detox/ios/EarlGrey/EarlGrey/Core/EarlGreyImpl.h': '../detox/src/ios/earlgreyapi/EarlGreyImpl.js',
'../detox/ios/Detox/GREYActions+Detox.h': '../detox/src/ios/earlgreyapi/GREYActions+Detox.js'
};

generateIOSAdapters(iosFiles);
Expand Down

0 comments on commit 6a1d700

Please sign in to comment.