Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WALLET-412: Run UI-based tests in CI #20

Merged
merged 31 commits into from
Aug 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
6122be6
Create Dockerfile for UI tests
NSeydoux Aug 8, 2024
e32694c
Add CI config to detox emulator
NSeydoux Aug 8, 2024
6def162
Enable hardware acceleration
NSeydoux Aug 12, 2024
c1b2817
Move files to root
NSeydoux Aug 12, 2024
139758a
Add UI tests to CI
NSeydoux Aug 12, 2024
663fb32
fixup! Add UI tests to CI
NSeydoux Aug 12, 2024
d5ad5fd
fixup! fixup! Add UI tests to CI
NSeydoux Aug 12, 2024
1169140
fixup! fixup! fixup! Add UI tests to CI
NSeydoux Aug 12, 2024
859b52a
fixup! fixup! fixup! fixup! Add UI tests to CI
NSeydoux Aug 12, 2024
73712d8
Add hardware acceleration check
NSeydoux Aug 12, 2024
bb7c8aa
Minimal test for hardware acceleration
NSeydoux Aug 12, 2024
f2702e6
fixup! Minimal test for hardware acceleration
NSeydoux Aug 12, 2024
330cfaf
fixup! fixup! Minimal test for hardware acceleration
NSeydoux Aug 12, 2024
c8d00fb
fixup! fixup! fixup! Minimal test for hardware acceleration
NSeydoux Aug 12, 2024
d246f71
fixup! fixup! fixup! fixup! Minimal test for hardware acceleration
NSeydoux Aug 12, 2024
d25d1b9
fixup! fixup! fixup! fixup! fixup! Minimal test for hardware accelera…
NSeydoux Aug 12, 2024
00f04e8
fixup! fixup! fixup! fixup! fixup! fixup! Minimal test for hardware a…
NSeydoux Aug 12, 2024
6316133
Get keystore config from environment
NSeydoux Aug 12, 2024
916f3ad
Remove HW acceleration experiment
NSeydoux Aug 12, 2024
5afcf54
Setup CI keystore configuration
NSeydoux Aug 12, 2024
49109c7
fixup! Remove HW acceleration experiment
NSeydoux Aug 12, 2024
2e08be6
fixup! Setup CI keystore configuration
NSeydoux Aug 12, 2024
a0a990e
fixup! fixup! Setup CI keystore configuration
NSeydoux Aug 12, 2024
ae0a285
fixup! fixup! fixup! Setup CI keystore configuration
NSeydoux Aug 12, 2024
32d4335
Document keystore management
NSeydoux Aug 12, 2024
c9bd891
Adjust Detox tests to run in Docker
NSeydoux Aug 13, 2024
194c5c2
Delete broken CI
NSeydoux Aug 14, 2024
3ff668b
Gitignore screenshots folder
NSeydoux Aug 14, 2024
6c76bb7
Added new env vars to env sample
NSeydoux Aug 14, 2024
03d8ddd
Delete Dockerfile and runner script
NSeydoux Aug 14, 2024
e235929
Remove references to Docker
NSeydoux Aug 20, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 19 additions & 2 deletions .detoxrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ module.exports = {
type: 'android.emulator',
device: {
avdName: process.env.TEST_ANDROID_EMU
}
},
}
},
configurations: {
Expand All @@ -69,6 +69,23 @@ module.exports = {
}
},
custom: {
defaultTestTimeout: 5000
defaultTestTimeout: 15000
},
artifacts: {
rootDir: "./screenshots/",
plugins: {
log: {"enabled": true},
uiHierarchy: {"enabled": true},
screenshot: {
keepOnlyFailedTestsArtifacts: false,
takeWhen: {
"testStart": true,
"testDone": true
}
},
video: {
enabled: true
}
}
}
};
2 changes: 2 additions & 0 deletions .env.sample
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,5 @@ TEST_IOS_SIM=<Name of the iOS simulator on your device>
# e.g. 'Android_34' or 'Pixel_3a_API_34'
TEST_ANDROID_EMU=<Name of the Android emulator on your device>
SIGNING_CONFIG_PATH=/absolute/path/to/android-config/signing-config.gradle
KEYSTORE_PATH=/absolute/path/to/wallet.keystore
KEYSTORE_PASSWORD=<keystore password>
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,4 +41,4 @@ jobs:
uses: kitabisa/[email protected]
with:
host: ${{ secrets.SONARQUBE_HOST }}
login: ${{ secrets.SONARQUBE_DEV_INRUPT_COM_GITHUB_TOKEN }}
login: ${{ secrets.SONARQUBE_DEV_INRUPT_COM_GITHUB_TOKEN }}
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,6 @@ expo-env.d.ts
.env

*.keystore

# Folder where the UI tests get stored.
screenshots/
23 changes: 16 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,14 +64,23 @@ keytool -genkeypair -v -storetype PKCS12 \
-noprompt -dname "CN=wallet.example.com"
```

Add the following to: `~/.gradle/gradle.properties` and update the placeholders.
Add the following to: `.env` and update the placeholders.
```text
WALLET_UPLOAD_STORE_FILE=<path>/inrupt-wallet-frontend/android/app/wallet.keystore
WALLET_UPLOAD_STORE_PASSWORD=<keystore password>
WALLET_UPLOAD_KEY_ALIAS=wallet
WALLET_UPLOAD_KEY_PASSWORD=<keystore password>
KEYSTORE_PATH=<path>/inrupt-wallet-frontend/android/app/wallet.keystore
KEYSTORE_PASSWORD=<keystore password>
```

#### Make the keystore available to CI

In order to make the keystore available to CI, it has to be present in the repository secret.
To
- Encrypting the keystore with a GPG key to get a Base64 representation: `gpg -c --armor wallet.keystore`
- Create Github repository secrets:
- ENCRYPTED_KEYSTORE with the Base64-encoded encrypted keystore
- KEYSTORE_DECRYPTION_KEY with the GPG key
- KEYSTORE_PASSWORD with the keystore password
- In CI, decrypt the keystore back: `gpg -d --passphrase "..." --batch wallet.keystore.asc > wallet.keystore`

## Running the application

If you are going to run the application in an emulator or simulator, you need to build the development version using
Expand Down Expand Up @@ -104,7 +113,7 @@ The tests require access credentials for a Pod which will be used by this instan
Make a copy of the provided `.env.sample` named `.env`, and replace placeholders with actual values
specific to your setup.

#### Running the tests on iOS
### Running the tests on iOS

To build the iOS wallet app in an iOS simulator, just run the following command:

Expand Down Expand Up @@ -134,7 +143,7 @@ Execute the command below to start Detox test on iOS.
npx detox test --configuration=ios.sim.release
```

#### Running the tests on Android
### Running the tests on Android

Ensure that a virtual device has been added to the Android emulator.

Expand Down
10 changes: 5 additions & 5 deletions android-config/signing-config.gradle
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
android {
signingConfigs {
release {
if (project.hasProperty('WALLET_UPLOAD_STORE_FILE')) {
storeFile file(WALLET_UPLOAD_STORE_FILE)
storePassword WALLET_UPLOAD_STORE_PASSWORD
keyAlias WALLET_UPLOAD_KEY_ALIAS
keyPassword WALLET_UPLOAD_KEY_PASSWORD
if (project.hasProperty('inrupt.wallet.frontend.keystore.file')) {
storeFile file(project.property('inrupt.wallet.frontend.keystore.file'))
storePassword project.property('inrupt.wallet.frontend.keystore.password')
keyAlias project.property('inrupt.wallet.frontend.key.alias')
keyPassword project.property('inrupt.wallet.frontend.key.password')
}
}
}
Expand Down
17 changes: 8 additions & 9 deletions e2e/pages/LoginPage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
import { by, element, expect, web } from "detox";

import detoxConfig from "../../.detoxrc";
import HomePage from "./HomePage";
import PermissionPage from "./PermissionPage";
Expand Down Expand Up @@ -58,20 +59,18 @@ class LoginPage {

// Perform login
await expect(this.signInFormUsernameInput).toExist();
await this.signInFormUsernameInput.runScript(`(element) => {
element.value = "${username}";
}`);
await this.signInFormUsernameInput.replaceText(username);
Comment on lines -61 to +62
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This feels like a more readable approach, using a more appropriate API for the task.


await expect(this.signInFormPasswordInput).toExist();
await this.signInFormPasswordInput.runScript(`(element) => {
element.value = "${password}";
}`);
await this.signInFormPasswordInput.replaceText(password);

await expect(this.signInSubmitButton).toExist();
await this.signInSubmitButton.runScript((buttonElement) => {
buttonElement.click();
await new Promise((resolve) => {
setTimeout(resolve, timeout * 5);
});

await expect(this.signInSubmitButton).toExist();
await this.signInSubmitButton.tap();
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ditto using a more expressive API for the given task.


// Handle permission screen
await new Promise((resolve) => {
setTimeout(resolve, timeout);
Expand Down
11 changes: 6 additions & 5 deletions e2e/pages/PermissionPage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,20 +18,21 @@
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//

import { expect, web, by } from "detox";

class PermissionPage {
private continueButton: Detox.WebElement;

constructor() {
this.continueButton = web.element(
by.web.xpath('//button[text()="Continue"]')
by.web.cssSelector('button[data-testid="prompt-continue"]')
Comment on lines -26 to +29
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using data-testid attributes, meant for automated tests and more stable than text content, is a good practice.

);
}

async clickContinueButton() {
// await waitFor(this.continueButton).toExist().withTimeout(5000);
await this.continueButton.runScript((element: HTMLButtonElement) => {
element.click();
});
await expect(this.continueButton).toExist();
await this.continueButton.tap();
}
}

Expand Down
36 changes: 33 additions & 3 deletions plugins/withSigningConfig.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,18 +28,48 @@ module.exports = function withSigningConfig(config) {
// Add path to gradle signing snippet to gradle.properties.
// This is then used by the android/app/build.gradle mod.
withGradleProperties(config, async (gradleProps) => {
if (process.env.SIGNING_CONFIG_PATH === undefined) {
throw new Error("Missing environment variable SIGNING_CONFIG_PATH");
}
gradleProps.modResults.push({
type: "comment",
value: "Path to the signing configuration gradle snippet",
});
if (process.env.SIGNING_CONFIG_PATH === undefined) {
throw new Error("Missing environment variable SIGNING_CONFIG_PATH");
}
gradleProps.modResults.push({
type: "property",
key: "inrupt.wallet.frontend.signing",
value: process.env.SIGNING_CONFIG_PATH,
});
if (process.env.KEYSTORE_PATH === undefined) {
throw new Error("Missing environment variable KEYSTORE_PATH");
}
if (process.env.KEYSTORE_PASSWORD === undefined) {
throw new Error("Missing environment variable KEYSTORE_PASSWORD");
}
gradleProps.modResults.push({
type: "comment",
value: "Keystore configuration for app signing.",
});
gradleProps.modResults.push({
type: "property",
key: "inrupt.wallet.frontend.keystore.file",
value: process.env.KEYSTORE_PATH,
});
gradleProps.modResults.push({
type: "property",
key: "inrupt.wallet.frontend.keystore.password",
value: process.env.KEYSTORE_PASSWORD,
});
gradleProps.modResults.push({
type: "property",
key: "inrupt.wallet.frontend.key.alias",
value: "wallet",
});
gradleProps.modResults.push({
type: "property",
key: "inrupt.wallet.frontend.key.password",
value: process.env.KEYSTORE_PASSWORD,
});
return gradleProps;
});
// Append a block to android/app/build.gradle to reuse signing config
Expand Down