Skip to content

Commit 8620368

Browse files
stefan-niedermannAndyScherzinger
authored andcommitted
#395 Add E2E test
Signed-off-by: Stefan Niedermann <[email protected]> Signed-off-by: Andy Scherzinger <[email protected]>
1 parent 915f45f commit 8620368

File tree

5 files changed

+283
-47
lines changed

5 files changed

+283
-47
lines changed

.github/workflows/e2e.yml

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
on: [push]
2+
3+
jobs:
4+
setup_nextcloud:
5+
runs-on: ubuntu-latest
6+
name: Run e2e test
7+
strategy:
8+
matrix:
9+
api-level: [ 24 ] #, 25, 26, 27, 28, 29 ]
10+
nextcloud-version: [ 'nextcloud:latest' ] #, 'nextcloud:stable', 'nextcloud:production' ]
11+
services:
12+
nextcloud:
13+
image: ${{ matrix.nextcloud-version }}
14+
env:
15+
SQLITE_DATABASE: db.sqlite
16+
NEXTCLOUD_ADMIN_USER: Test
17+
NEXTCLOUD_ADMIN_PASSWORD: Test
18+
ports:
19+
- 8080:80
20+
options: >-
21+
--health-cmd "curl GET 'http://Test:Test@localhost:80/ocs/v2.php/apps/serverinfo/api/v1/info' -f -H 'OCS-APIRequest: true' || exit 1"
22+
--health-interval 1s
23+
--health-timeout 2s
24+
--health-retries 10
25+
--health-start-period 3s
26+
steps:
27+
- name: Checkout
28+
uses: actions/checkout@v2
29+
30+
- name: Make Nextcloud accessible from AVD
31+
run: |
32+
docker exec `docker ps -f 'name=_nextcloud' -l -q` bash -c 'runuser -u www-data -- php occ config:system:set trusted_domains 2 --value=172.17.0.1'
33+
34+
# TODO 172.17.0.1 is the hard coded IP address of the docker container. Make this more generic.
35+
- name: Verify Nextcloud being present on 172.17.0.1
36+
run: |
37+
curl -v -X GET 'http://Test:[email protected]:8080/ocs/v2.php/cloud/capabilities?format=json' -H 'OCS-APIRequest: true' | jq
38+
39+
##########################
40+
# AVD CACHING START #
41+
##########################
42+
43+
- name: Gradle cache
44+
uses: actions/cache@v2
45+
with:
46+
path: |
47+
~/.gradle/caches
48+
~/.gradle/wrapper
49+
key: gradle-${{ runner.os }}-${{ hashFiles('**/*.gradle*') }}-${{ hashFiles('**/gradle/wrapper/gradle-wrapper.properties') }}-${{ hashFiles('**/buildSrc/**/*.kt') }}
50+
51+
- name: AVD cache
52+
uses: actions/cache@v2
53+
id: avd-cache
54+
with:
55+
path: |
56+
~/.android/avd/*
57+
~/.android/adb*
58+
key: avd-${{ matrix.api-level }}
59+
60+
- name: Create AVD and generate snapshot for caching
61+
if: steps.avd-cache.outputs.cache-hit != 'true'
62+
uses: reactivecircus/android-emulator-runner@v2
63+
with:
64+
api-level: ${{ matrix.api-level }}
65+
force-avd-creation: false
66+
sdcard-path-or-size: sdcard
67+
emulator-options: -gpu swiftshader_indirect -no-window -noaudio -no-boot-anim -camera-back none
68+
disable-animations: true
69+
script: echo "Generated AVD snapshot for caching."
70+
71+
##########################
72+
# AVD CACHING END #
73+
##########################
74+
75+
- name: Run e2e tests
76+
uses: reactivecircus/android-emulator-runner@v2
77+
with:
78+
api-level: ${{ matrix.api-level }}
79+
force-avd-creation: false
80+
emulator-options: -no-snapshot-save -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none
81+
disable-animations: true
82+
# FIXME Execution of connectedDebugAndroidTest fails
83+
# TODO latest.apk should be cached when matrix testing is used
84+
script: |
85+
adb shell pm uninstall -k --user 0 com.nextcloud.android.beta || true
86+
wget -q https://download.nextcloud.com/android/dev/latest.apk
87+
adb install latest.apk
88+
adb shell pm grant com.nextcloud.android.beta android.permission.READ_EXTERNAL_STORAGE
89+
adb logcat -c || true
90+
adb logcat *:I -v color &
91+
./gradlew connectedDebugAndroidTest || true

sample/build.gradle

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,6 @@ dependencies {
3838
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
3939
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
4040

41-
testImplementation 'junit:junit:4.13.2'
42-
43-
androidTestImplementation 'androidx.test.ext:junit:1.1.5'
44-
androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
41+
androidTestImplementation 'androidx.test:runner:1.4.0'
42+
androidTestImplementation 'androidx.test.uiautomator:uiautomator:2.2.0'
4543
}
Lines changed: 190 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,190 @@
1+
package com.nextcloud.android.sso.sample;
2+
3+
import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
4+
import static androidx.test.uiautomator.Until.findObject;
5+
import static androidx.test.uiautomator.Until.hasObject;
6+
7+
import android.content.Intent;
8+
import android.util.Log;
9+
import android.webkit.WebView;
10+
import android.widget.Button;
11+
import android.widget.EditText;
12+
13+
import androidx.annotation.NonNull;
14+
import androidx.test.uiautomator.By;
15+
import androidx.test.uiautomator.UiDevice;
16+
import androidx.test.uiautomator.UiObjectNotFoundException;
17+
import androidx.test.uiautomator.UiSelector;
18+
19+
import org.junit.After;
20+
import org.junit.Before;
21+
import org.junit.FixMethodOrder;
22+
import org.junit.Test;
23+
import org.junit.runners.MethodSorters;
24+
25+
/**
26+
* FIXME This does not yet work
27+
*/
28+
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
29+
public class E2ETest {
30+
31+
private static final String TAG = E2ETest.class.getSimpleName();
32+
33+
private UiDevice mDevice;
34+
35+
private static final int TIMEOUT = 60_000;
36+
37+
private static final String APP_NEXTCLOUD = "com.nextcloud.android.beta";
38+
// TODO This should be passed as argument
39+
private static final String APP_SAMPLE = BuildConfig.APPLICATION_ID;
40+
private static final String SERVER_URL = "http://172.17.0.1:8080";
41+
private static final String SERVER_USERNAME = "Test";
42+
private static final String SERVER_PASSWORD = "Test";
43+
44+
@Before
45+
public void before() {
46+
mDevice = UiDevice.getInstance(getInstrumentation());
47+
mDevice.pressHome();
48+
}
49+
50+
@After
51+
public void after() {
52+
mDevice.pressHome();
53+
}
54+
55+
@Test
56+
public void test_00_configureNextcloudAccount() throws UiObjectNotFoundException {
57+
launch(APP_NEXTCLOUD);
58+
59+
final var loginButton = mDevice.findObject(new UiSelector().textContains("Log in"));
60+
loginButton.waitForExists(TIMEOUT);
61+
log("Login Button exists. Clicking on it...");
62+
loginButton.click();
63+
log("Login Button clicked.");
64+
65+
final var urlInput = mDevice.findObject(new UiSelector().focused(true));
66+
urlInput.waitForExists(TIMEOUT);
67+
log("URL input exists.");
68+
log("Entering URL...");
69+
urlInput.setText(SERVER_URL);
70+
log("URL entered.");
71+
72+
log("Pressing enter...");
73+
mDevice.pressEnter();
74+
log("Enter pressed.");
75+
76+
log("Waiting for WebView...");
77+
mDevice.wait(findObject(By.clazz(WebView.class)), TIMEOUT);
78+
log("WebView exists.");
79+
80+
final var webViewLoginButton = mDevice.findObject(new UiSelector()
81+
.instance(0)
82+
.className(Button.class));
83+
log("Waiting for WebView Login Button...");
84+
webViewLoginButton.waitForExists(TIMEOUT);
85+
log("WebView Login Button exists. Clicking on it...");
86+
webViewLoginButton.click();
87+
88+
final var usernameInput = mDevice.findObject(new UiSelector()
89+
.instance(0)
90+
.className(EditText.class));
91+
log("Waiting for Username Input...");
92+
usernameInput.waitForExists(TIMEOUT);
93+
log("Username Input exists. Setting text...");
94+
usernameInput.setText(SERVER_USERNAME);
95+
log("Username has been set.");
96+
97+
final var passwordInput = mDevice.findObject(new UiSelector()
98+
.instance(1)
99+
.className(EditText.class));
100+
log("Waiting for Password Input...");
101+
passwordInput.waitForExists(TIMEOUT);
102+
log("Password Input exists. Setting text...");
103+
passwordInput.setText(SERVER_PASSWORD);
104+
105+
final var webViewSubmitButton = mDevice.findObject(new UiSelector()
106+
.instance(0)
107+
.className(Button.class));
108+
log("Waiting for WebView Submit Button...");
109+
webViewSubmitButton.waitForExists(TIMEOUT);
110+
log("WebView Submit Button exists. Clicking on it...");
111+
webViewSubmitButton.click();
112+
113+
final var webViewGrantAccessButton = mDevice.findObject(new UiSelector()
114+
.instance(0)
115+
.className(Button.class));
116+
log("Waiting for WebView Grant Access Button...");
117+
webViewGrantAccessButton.waitForExists(TIMEOUT);
118+
log("WebView Grant Access Button exists. Clicking on it...");
119+
webViewGrantAccessButton.click();
120+
}
121+
122+
@Test
123+
public void test_01_importAccountIntoSampleApp() throws UiObjectNotFoundException, InterruptedException {
124+
launch(APP_SAMPLE);
125+
126+
final var accountButton = mDevice.findObject(new UiSelector()
127+
.instance(0)
128+
.className(Button.class));
129+
accountButton.waitForExists(TIMEOUT);
130+
accountButton.click();
131+
132+
mDevice.waitForWindowUpdate(null, TIMEOUT);
133+
134+
final var radioAccount = mDevice.findObject(new UiSelector()
135+
.clickable(true)
136+
.instance(0));
137+
radioAccount.waitForExists(TIMEOUT);
138+
radioAccount.click();
139+
140+
mDevice.waitForWindowUpdate(null, TIMEOUT);
141+
142+
Thread.sleep(10_000);
143+
final var okButton = mDevice.findObject(new UiSelector()
144+
.textContains("OK"));
145+
log("Waiting for OK Button...");
146+
okButton.waitForExists(TIMEOUT);
147+
log("OK Button exists. Clicking on it...");
148+
okButton.click();
149+
log("OK Button clicked");
150+
151+
mDevice.waitForWindowUpdate(null, TIMEOUT);
152+
153+
final var allowButton = mDevice.findObject(new UiSelector()
154+
.instance(1)
155+
.className(Button.class));
156+
log("Waiting for Allow Button...");
157+
allowButton.waitForExists(TIMEOUT);
158+
log("Allow Button exists. Clicking on it...");
159+
allowButton.click();
160+
log("Allow Button clicked");
161+
162+
log("Waiting for finished import...");
163+
final var welcomeText = mDevice.findObject(new UiSelector().description("Filter"));
164+
welcomeText.waitForExists(TIMEOUT);
165+
log("Import finished.");
166+
}
167+
168+
@Test
169+
public void test_02_verifyResult() throws UiObjectNotFoundException {
170+
launch(APP_SAMPLE);
171+
172+
final var taskCard = mDevice.findObject(new UiSelector()
173+
.textContains("Test on Nextcloud"));
174+
taskCard.waitForExists(TIMEOUT);
175+
System.out.println("Found: " + taskCard.getText());
176+
}
177+
178+
private void log(@NonNull String message) {
179+
Log.i(TAG, message);
180+
}
181+
182+
private void launch(@NonNull String packageName) {
183+
final var context = getInstrumentation().getContext();
184+
context.startActivity(context
185+
.getPackageManager()
186+
.getLaunchIntentForPackage(packageName)
187+
.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK));
188+
mDevice.wait(hasObject(By.pkg(packageName).depth(0)), TIMEOUT);
189+
}
190+
}

sample/src/androidTest/java/com/nextcloud/android/sso/sample/ExampleInstrumentedTest.java

Lines changed: 0 additions & 26 deletions
This file was deleted.

sample/src/test/java/com/nextcloud/android/sso/sample/ExampleUnitTest.java

Lines changed: 0 additions & 17 deletions
This file was deleted.

0 commit comments

Comments
 (0)