Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
3 changes: 2 additions & 1 deletion .semaphore/semaphore.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@ blocks:
- ios-sim start --devicetypeid com.apple.CoreSimulator.SimDeviceType.iPhone-13
- bundle exec fastlane build && ls -la appium
- cd appium && npm run-script lint
- npm run-script only.test.all
- npm run-script test.mock.all
- npm run-script test.live.all
epilogue:
always:
commands:
Expand Down
12 changes: 12 additions & 0 deletions FlowCrypt.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -361,6 +361,10 @@
D2FD0F692453245E00259FF0 /* Either.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2FD0F682453245E00259FF0 /* Either.swift */; };
D2FF6966243115EC007182F0 /* SetupImapViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2FF6965243115EC007182F0 /* SetupImapViewController.swift */; };
D2FF6968243115F9007182F0 /* SetupImapViewDecorator.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2FF6967243115F9007182F0 /* SetupImapViewDecorator.swift */; };
E26D5E1F275AA295007B8802 /* CommandLineExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = E26D5E1E275AA295007B8802 /* CommandLineExtensions.swift */; };
E26D5E21275AA417007B8802 /* BundleExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = E26D5E20275AA417007B8802 /* BundleExtensions.swift */; };
E26D5E22275AA421007B8802 /* BundleExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = E26D5E20275AA417007B8802 /* BundleExtensions.swift */; };
E26D5E23275AA42B007B8802 /* CommandLineExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = E26D5E1E275AA295007B8802 /* CommandLineExtensions.swift */; };
F191F621272511790053833E /* BlurViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F191F620272511790053833E /* BlurViewController.swift */; };
F80E95362720B6640093F243 /* DraftsListProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = F80E95352720B6640093F243 /* DraftsListProvider.swift */; };
F8678DCC2722143300BB1710 /* GmailService+draft.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8678DCB2722143300BB1710 /* GmailService+draft.swift */; };
Expand Down Expand Up @@ -768,6 +772,8 @@
D2FF6965243115EC007182F0 /* SetupImapViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SetupImapViewController.swift; sourceTree = "<group>"; };
D2FF6967243115F9007182F0 /* SetupImapViewDecorator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SetupImapViewDecorator.swift; sourceTree = "<group>"; };
D9381A4EE6BAAD97294A7F9A /* Pods-FlowCryptUI.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-FlowCryptUI.release.xcconfig"; path = "Target Support Files/Pods-FlowCryptUI/Pods-FlowCryptUI.release.xcconfig"; sourceTree = "<group>"; };
E26D5E1E275AA295007B8802 /* CommandLineExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CommandLineExtensions.swift; sourceTree = "<group>"; };
E26D5E20275AA417007B8802 /* BundleExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BundleExtensions.swift; sourceTree = "<group>"; };
F191F620272511790053833E /* BlurViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BlurViewController.swift; sourceTree = "<group>"; };
F80E95352720B6640093F243 /* DraftsListProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DraftsListProvider.swift; sourceTree = "<group>"; };
F8678DCB2722143300BB1710 /* GmailService+draft.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "GmailService+draft.swift"; sourceTree = "<group>"; };
Expand Down Expand Up @@ -1002,7 +1008,9 @@
isa = PBXGroup;
children = (
D2D27B78248A8694007346FA /* BigIntExtension.swift */,
E26D5E20275AA417007B8802 /* BundleExtensions.swift */,
9F2F217226B3269D0044E144 /* CombineExtensions.swift */,
E26D5E1E275AA295007B8802 /* CommandLineExtensions.swift */,
51E4F0B427348E310017DABB /* Error+Extension.swift */,
51B0C7702729861C00124663 /* String+Extension.swift */,
518389C92726D8F700131B2C /* UIApplicationExtension.swift */,
Expand Down Expand Up @@ -2476,6 +2484,7 @@
9F976584267E194F0058419D /* TestData.swift in Sources */,
9F6F3C6A26ADFBEB005BD9C6 /* MessageGatewayMock.swift in Sources */,
9F7E903926A1AD7A0021C07F /* KeyDetailsTests.swift in Sources */,
E26D5E23275AA42B007B8802 /* CommandLineExtensions.swift in Sources */,
51B0C774272AB61000124663 /* StringTestExtension.swift in Sources */,
2C2A3B4B2719EE6100B7F27B /* KeyServiceTests.swift in Sources */,
9F976490267E11880058419D /* ImapHelperTest.swift in Sources */,
Expand Down Expand Up @@ -2509,6 +2518,7 @@
9F5F503526F90E5F00294FA2 /* ClientConfigurationServiceTests.swift in Sources */,
9F2F206826AEEAA60044E144 /* CombineTestExtension.swift in Sources */,
9F976585267E194F0058419D /* FlowCryptCoreTests.swift in Sources */,
E26D5E22275AA421007B8802 /* BundleExtensions.swift in Sources */,
9F6F3C7D26ADFC60005BD9C6 /* ContactsServiceMock.swift in Sources */,
9F6F3C3C26ADFBC7005BD9C6 /* CoreComposeMessageMock.swift in Sources */,
9FC4116B2681186D004C0A69 /* KeyMethodsTest.swift in Sources */,
Expand Down Expand Up @@ -2670,6 +2680,7 @@
9F9AAFFD2383E216000A00F1 /* Document.swift in Sources */,
9FE1B3A02565B0CE00D6D086 /* Message.swift in Sources */,
D27177492424D73000BDA9A9 /* InboxViewDecorator.swift in Sources */,
E26D5E1F275AA295007B8802 /* CommandLineExtensions.swift in Sources */,
D2F41371243CC76F0066AFB5 /* SessionRealmObject.swift in Sources */,
9F9362192573D10E0009912F /* Imap+Message.swift in Sources */,
32DCA9C61ABB3234649B374E /* CoreHost.swift in Sources */,
Expand Down Expand Up @@ -2734,6 +2745,7 @@
9F53CB872555E7F300C0157A /* Imap+Other.swift in Sources */,
D2F6D1352433753B00DB4065 /* SMTPSession.swift in Sources */,
9FBEAF3125DFB8E1009E98D4 /* DBMigrationService.swift in Sources */,
E26D5E21275AA417007B8802 /* BundleExtensions.swift in Sources */,
9F17976D2368EEBD002BF770 /* SetupViewDecorator.swift in Sources */,
5ADEDCC023A43B0800EC495E /* KeyDetailInfoViewDecorator.swift in Sources */,
D227C0E6250538780070F805 /* RemoteFoldersProvider.swift in Sources */,
Expand Down
24 changes: 24 additions & 0 deletions FlowCrypt/Extensions/BundleExtensions.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
//
// AppBundle.swift
// FlowCrypt
//
// Created by Tom on 03.12.2021
// Copyright © 2017-present FlowCrypt a. s. All rights reserved.
//

import Foundation

enum FlowCryptBundleType: String {
case debug = "com.flowcrypt.as.ios.debug",
consumer = "com.flowcrypt.as.ios.consumer",
enterprise = "com.flowcrypt.as.ios.enterprise"
}

extension Bundle {

static var flowCryptBundleType: FlowCryptBundleType {
guard let bundleIdentifier = Bundle.main.bundleIdentifier else { return .debug }
return FlowCryptBundleType(rawValue: bundleIdentifier) ?? .debug
}

}
20 changes: 20 additions & 0 deletions FlowCrypt/Extensions/CommandLineExtensions.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
//
// CommandLineExtensions.swift
// FlowCrypt
//
// Created by Tom on 03.12.2021
// Copyright © 2017-present FlowCrypt a. s. All rights reserved.
//



import Foundation

extension CommandLine {

static func isDebugBundleWithArgument(_ argument: String) -> Bool {
guard Bundle.flowCryptBundleType == .debug else { return false }
return CommandLine.arguments.contains(argument)
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ extension EnterpriseServerApiError: LocalizedError {
class EnterpriseServerApi: EnterpriseServerApiType {

static let publicEmailProviderDomains = ["gmail.com", "googlemail.com", "outlook.com"]

private enum Constants {
/// 404 - Not Found
static let getToleratedHTTPStatuses = [404]
Expand All @@ -52,16 +52,23 @@ class EnterpriseServerApi: EnterpriseServerApiType {
let clientConfiguration: RawClientConfiguration
}

private func constructUrlBase(emailDomain: String) -> String {
guard !CommandLine.isDebugBundleWithArgument("--mock-fes-api") else {
return "http://127.0.0.1:8001/fes" // mock
}
return "https://fes.\(emailDomain)" // live
}

func getActiveFesUrl(for email: String) async throws -> String? {
do {
guard let userDomain = email.recipientDomain,
!EnterpriseServerApi.publicEmailProviderDomains.contains(userDomain) else {
return nil
}
let urlString = "https://fes.\(userDomain)/"
let urlBase = constructUrlBase(emailDomain: userDomain)
let request = ApiCall.Request(
apiName: Constants.apiName,
url: "\(urlString)api/",
url: "\(urlBase)/api/",
timeout: Constants.getActiveFesTimeout,
tolerateStatus: Constants.getToleratedHTTPStatuses
)
Expand All @@ -77,7 +84,7 @@ class EnterpriseServerApi: EnterpriseServerApiType {
return nil
}

return urlString
return urlBase
} catch {
if let httpError = error as? HttpErr,
let nsError = httpError.error as NSError?,
Expand All @@ -93,16 +100,13 @@ class EnterpriseServerApi: EnterpriseServerApiType {
throw EnterpriseServerApiError.emailFormat
}

guard try await getActiveFesUrl(for: email) != nil else {
guard let fesUrl = try await getActiveFesUrl(for: email) else {
return .empty
}

if EnterpriseServerApi.publicEmailProviderDomains.contains(userDomain) {
return .empty
}
let request = ApiCall.Request(
apiName: Constants.apiName,
url: "https://fes.\(userDomain)/api/v1/client-configuration?domain=\(userDomain)"
url: "\(fesUrl)/api/v1/client-configuration?domain=\(userDomain)"
)
let safeReponse = try await ApiCall.call(request)

Expand Down
14 changes: 9 additions & 5 deletions appium/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,16 @@
6. `nvm install 16` - installs NodeJS 16 and sets it as default
7. `cd ~/git/flowcrypt-ios/appium && npm install`

## Run tests
## Building app for testing

Run this in `flowcrypt-ios` folder: `bundle exec fastlane build`. This will produce folder `appium/FlowCrypt.app` that contains the built app.

Run this in `appium` folder.
## Run tests

`npm test` - build `FlowCrypt.app` and run all ui tests
Run these in `appium` folder. `live` means real production APIs, `mock` means local mock APIs.

`npm run-script only.test.all` - run all ui tests without building the `.app`. Use this if you already built the `.app` before, and now only want to change the UI test spec without rebuilding the app
To run a particular test:
- `npm run-script test.live "user is able to view text email"`
- `npm run-script test.mock "app setup fails with bad EKM URL"`
Comment on lines +21 to +23
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

@sosnovsky @fcvakintos @ivan-ushakov @Kharchevskyi @ekievsky I have updated the commands to run tests.

So instead of npm run-script only.test.filter "some test"
it now is npn run-script test.live "some test".


`npm run-script only.test.filter "user is able to view text email"` - run a particular ui test without building `.app`, filtered by name
To run all tests: `npm run-script test.live.all` or `npm run-script test.mock.all`
27 changes: 19 additions & 8 deletions appium/api-mocks/apis/attester/attester-endpoints.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,22 @@
/* ©️ 2016 - present FlowCrypt a.s. Limitations apply. Contact human@flowcrypt.com */

import { HandlersDefinition, HttpClientErr } from '../../lib/api';
import { HandlersDefinition, HttpErr, Status } from '../../lib/api';
import { Dict } from '../../core/common';
import { isPost, isGet } from '../../lib/mock-util';
import { oauth } from '../../lib/oauth';
import { AttesterConfig, MockConfig } from '../../lib/configuration-types';

export class AttesterErr extends HttpErr {
public formatted = (): unknown => {
return { // follows Attester errror format
error: {
"code": this.statusCode,
"message": this.message,
}
}
}
}

export const MOCK_ATTESTER_LAST_INSERTED_PUB: { [email: string]: string } = {};

export const getMockAttesterEndpoints = (
Expand All @@ -25,31 +36,31 @@ export const getMockAttesterEndpoints = (
if (pubkey) {
return pubkey;
}
throw new HttpClientErr('Pubkey not found on mock', 404);
throw new AttesterErr('Pubkey not found on mock', 404);
} else if (isPost(req)) {
if (attesterConfig.enableSubmittingPubkeys !== true) {
throw new HttpClientErr('Mock Attester received unexpected pubkey submission', 405);
throw new AttesterErr('Mock Attester received unexpected pubkey submission', 405);
}
oauth.checkAuthorizationHeaderWithIdToken(req.headers.authorization);
if (!(body as string).includes('-----BEGIN PGP PUBLIC KEY BLOCK-----')) {
throw new HttpClientErr(`Bad public key format`, 400);
throw new AttesterErr(`Bad public key format`, 400);
}
MOCK_ATTESTER_LAST_INSERTED_PUB[email] = body as string;
return 'Saved'; // 200 OK
} else {
throw new HttpClientErr(`Not implemented: ${req.method}`);
throw new AttesterErr(`Not implemented: ${req.method}`, Status.BAD_REQUEST);
}
},
'/attester/test/welcome': async ({ body }, req) => {
if (!isPost(req)) {
throw new HttpClientErr(`Wrong method: ${req.method}`);
throw new AttesterErr(`Wrong method: ${req.method}`, Status.BAD_REQUEST);
}
const { email, pubkey } = body as Dict<string>;
if (email.includes('@')) {
throw new HttpClientErr(`Bad email format`, 400);
throw new AttesterErr(`Bad email format`, 400);
}
if (pubkey.includes('-----BEGIN PGP PUBLIC KEY BLOCK-----')) {
throw new HttpClientErr(`Bad public key format`, 400);
throw new AttesterErr(`Bad public key format`, 400);
}
return { sent: true };
},
Expand Down
22 changes: 16 additions & 6 deletions appium/api-mocks/apis/ekm/ekm-endpoints.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,30 @@
/* ©️ 2016 - present FlowCrypt a.s. Limitations apply. Contact human@flowcrypt.com */

// import { HttpClientErr } from '../lib/api';
// import { HttpErr } from '../lib/api';
// import { isPut, isGet } from '../lib/mock-util';
// import { oauth } from '../lib/oauth';
// import { Dict } from '../../core/common';
// import { expect } from 'chai';
// import { KeyUtil } from '../../core/crypto/key';
// import { testConstants } from '../../tests/tooling/consts';
import { HandlersDefinition } from '../../lib/api';
import { HandlersDefinition, HttpErr } from '../../lib/api';
import { EkmConfig, MockConfig } from '../../lib/configuration-types';

// tslint:disable:max-line-length
/* eslint-disable max-len */
// tslint:disable:no-unused-expression
/* eslint-disable no-unused-expressions */

export class EkmHttpErr extends HttpErr {
public formatted = (): unknown => {
return { // follows EKM error response format
"code": this.statusCode,
"message": `message:${this.message}`,
"details": `details:${this.message}`
}
}
}

export const MOCK_KM_LAST_INSERTED_KEY: { [acct: string]: { decryptedPrivateKey: string, publicKey: string } } = {}; // accessed from test runners

/**
Expand Down Expand Up @@ -74,7 +84,7 @@ export const getMockEkmEndpoints = (
// if (acctEmail === 'get.error@key-manager-autogen.flowcrypt.test') {
// throw new Error('Intentional error for get.error to test client behavior');
// }
// throw new HttpClientErr(`Unexpectedly calling mockKeyManagerEndpoints:/keys/private GET with acct ${acctEmail}`);
// throw new HttpErr(`Unexpectedly calling mockKeyManagerEndpoints:/keys/private GET with acct ${acctEmail}`);
// }
// if (isPut(req)) {
// const { decryptedPrivateKey, publicKey } = body as Dict<string>;
Expand Down Expand Up @@ -102,7 +112,7 @@ export const getMockEkmEndpoints = (
// throw new Error('Intentional error for put.error user to test client behavior');
// }
// if (acctEmail === 'reject.client.keypair@key-manager-autogen.flowcrypt.test') {
// throw new HttpClientErr(`No key has been generated for ${acctEmail} yet. Please ask your administrator.`, 405);
// throw new HttpErr(`No key has been generated for ${acctEmail} yet. Please ask your administrator.`, 405);
// }
// if (acctEmail === 'expire@key-manager-keygen-expiration.flowcrypt.test') {
// const prv = await KeyUtil.parseMany(decryptedPrivateKey);
Expand All @@ -124,9 +134,9 @@ export const getMockEkmEndpoints = (
// MOCK_KM_LAST_INSERTED_KEY[acctEmail] = { decryptedPrivateKey, publicKey };
// return {};
// }
// throw new HttpClientErr(`Unexpectedly calling mockKeyManagerEndpoints:/keys/private PUT with acct ${acctEmail}`);
// throw new HttpErr(`Unexpectedly calling mockKeyManagerEndpoints:/keys/private PUT with acct ${acctEmail}`);
// }
// throw new HttpClientErr(`Unknown method: ${req.method}`);
// throw new HttpErr(`Unknown method: ${req.method}`);
// }
// };
}
Expand Down
Loading