* Squash into one commit

Signed-off-by: Milen Pivchev <milen.pivchev@gmail.com>

* Refactor

Signed-off-by: Milen Pivchev <milen.pivchev@gmail.com>

* Put script in a gist

Signed-off-by: Milen Pivchev <milen.pivchev@gmail.com>

* Exclude tests from lint

Signed-off-by: Milen Pivchev <milen.pivchev@gmail.com>

* Add README info for tests

Signed-off-by: Milen Pivchev <milen.pivchev@gmail.com>

* Remove local script

Signed-off-by: Milen Pivchev <milen.pivchev@gmail.com>

* New gist

Signed-off-by: Milen Pivchev <milen.pivchev@gmail.com>

* Revert "Remove local script"

This reverts commit b0da803377.

Signed-off-by: Milen Pivchev <milen.pivchev@gmail.com>

* Restore local script

Signed-off-by: Milen Pivchev <milen.pivchev@gmail.com>

* Update README

Signed-off-by: Milen Pivchev <milen.pivchev@gmail.com>

* Fix test

Signed-off-by: Milen Pivchev <milen.pivchev@gmail.com>

* Refactor

Signed-off-by: Milen Pivchev <milen.pivchev@gmail.com>

* Remove code

Signed-off-by: Milen Pivchev <milen.pivchev@gmail.com>

* Swiftlint

Signed-off-by: Milen Pivchev <milen.pivchev@gmail.com>

* Refactor

Signed-off-by: Milen Pivchev <milen.pivchev@gmail.com>

* Update Tests/NextcloudUITests/LoginUITests.swift

Co-authored-by: Marcel Müller <marcel-mueller@gmx.de>
Signed-off-by: Milen Pivchev <milen.pivchev@gmail.com>

* Refactor

Signed-off-by: Milen Pivchev <milen.pivchev@gmail.com>

* Try to fix failing test

Signed-off-by: Milen Pivchev <milen.pivchev@gmail.com>

* Add base UI test class

Signed-off-by: Milen Pivchev <milen.pivchev@gmail.com>

* Use docker set up alternative

Signed-off-by: Milen Pivchev <milen.pivchev@gmail.com>

---------

Signed-off-by: Milen Pivchev <milen.pivchev@gmail.com>
Co-authored-by: Marcel Müller <marcel-mueller@gmx.de>
This commit is contained in:
Milen Pivchev 2023-06-22 15:59:55 +02:00 коммит произвёл GitHub
Родитель 967ea23907
Коммит 422440e1a9
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
33 изменённых файлов: 1661 добавлений и 139 удалений

68
.github/workflows/additional-targets.yml поставляемый Normal file
Просмотреть файл

@ -0,0 +1,68 @@
name: Build additional targets
on:
push:
branches:
- master
- develop
pull_request:
types: [synchronize, opened, reopened, ready_for_review]
branches:
- master
- develop
jobs:
build-and-test:
name: Build and Test
runs-on: macOS-latest
if: github.event.pull_request.draft == false
env:
PROJECT: Nextcloud.xcodeproj
DESTINATION: platform=iOS Simulator,name=iPhone 14
steps:
- name: Set env var
run: echo "DEVELOPER_DIR=$(xcode-select --print-path)" >> $GITHUB_ENV
- uses: actions/checkout@v3
- name: Setup Bundler and Install Gems
run: |
gem install bundler
bundle install
bundle update
- name: Restore Carhage Cache
uses: actions/cache@v3
id: carthage-cache
with:
path: Carthage
key: ${{ runner.os }}-carthage-${{ hashFiles('**/Cartfile.resolved') }}
restore-keys: |
${{ runner.os }}-carthage-
- name: Carthage
if: steps.carthage-cache.outputs.cache-hit != 'true'
run: carthage bootstrap --use-xcframeworks --platform iOS
- name: Download GoogleService-Info.plist
run: wget "https://raw.githubusercontent.com/firebase/quickstart-ios/master/mock-GoogleService-Info.plist" -O GoogleService-Info.plist
- name: Build iOS Share
run: |
xcodebuild build -project $PROJECT -scheme "$SCHEME" -destination "$DESTINATION"
env:
SCHEME: Share
- name: Build iOS File Extension
run: |
xcodebuild build -project $PROJECT -scheme "$SCHEME" -destination "$DESTINATION"
env:
SCHEME: File Provider Extension
- name: Build iOS Notification Extension
run: |
xcodebuild build -project $PROJECT -scheme "$SCHEME" -destination "$DESTINATION"
env:
SCHEME: Notification Service Extension
- name: Build iOS Widget
run: |
xcodebuild build -project $PROJECT -scheme "$SCHEME" -destination "$DESTINATION"
env:
SCHEME: Widget
- name: Build iOS Widget Dashboard IntentHandler
run: |
xcodebuild build -project $PROJECT -scheme "$SCHEME" -destination "$DESTINATION"
env:
SCHEME: WidgetDashboardIntentHandler

2
.github/workflows/lint.yml поставляемый
Просмотреть файл

@ -20,7 +20,7 @@ jobs:
if: github.event.pull_request.draft == false
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- name: GitHub Action for SwiftLint
uses: norio-nomura/action-swiftlint@3.1.0

65
.github/workflows/xcode.yml поставляемый
Просмотреть файл

@ -1,4 +1,4 @@
name: Build
name: Build main target
on:
push:
@ -12,18 +12,25 @@ on:
- develop
jobs:
XCBuild:
build-and-test:
name: Build and Test
runs-on: macOS-latest
if: github.event.pull_request.draft == false
env:
PROJECT: Nextcloud.xcodeproj
DESTINATION: platform=iOS Simulator,name=iPhone 14
SCHEME: Nextcloud
steps:
- name: Set env var
run: echo "DEVELOPER_DIR=$(xcode-select --print-path)" >> $GITHUB_ENV
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- name: Setup Bundler and Install Gems
run: |
gem install bundler
bundle install
bundle update
- name: Restore Carhage Cache
uses: actions/cache@v2
uses: actions/cache@v3
id: carthage-cache
with:
path: Carthage
@ -35,34 +42,30 @@ jobs:
run: carthage bootstrap --use-xcframeworks --platform iOS
- name: Download GoogleService-Info.plist
run: wget "https://raw.githubusercontent.com/firebase/quickstart-ios/master/mock-GoogleService-Info.plist" -O GoogleService-Info.plist
- name: Install docker
run: |
brew install docker
colima start
- name: Create docker test server and export enviroment variables
run: |
source ./create-docker-test-server.sh
if [ ! -f ".env-vars" ]; then
touch .env-vars
echo "export TEST_SERVER_URL=$TEST_SERVER_URL" >> .env-vars
echo "export TEST_USER=$TEST_USER" >> .env-vars
echo "export TEST_APP_PASSWORD=$TEST_APP_PASSWORD" >> .env-vars
fi
- name: Build & Test Nextcloud iOS
run: |
xcodebuild clean build test -project $PROJECT -scheme "$SCHEME" -destination "$DESTINATION"
env:
SCHEME: Nextcloud
- name: Build iOS Share
set -o pipefail && xcodebuild test -project $PROJECT \
-scheme "$SCHEME" \
-destination "$DESTINATION" \
-enableCodeCoverage YES \
-test-iterations 3 \
-retry-tests-on-failure \
| xcpretty
- name: Upload coverage to codecov
run: |
xcodebuild build -project $PROJECT -scheme "$SCHEME" -destination "$DESTINATION"
env:
SCHEME: Share
- name: Build iOS File Extension
run: |
xcodebuild build -project $PROJECT -scheme "$SCHEME" -destination "$DESTINATION"
env:
SCHEME: File Provider Extension
- name: Build iOS Notification Extension
run: |
xcodebuild build -project $PROJECT -scheme "$SCHEME" -destination "$DESTINATION"
env:
SCHEME: Notification Service Extension
- name: Build iOS Widget
run: |
xcodebuild build -project $PROJECT -scheme "$SCHEME" -destination "$DESTINATION"
env:
SCHEME: Widget
- name: Build iOS Widget Dashboard IntentHandler
run: |
xcodebuild build -project $PROJECT -scheme "$SCHEME" -destination "$DESTINATION"
env:
SCHEME: WidgetDashboardIntentHandler
bundle exec slather
bash <(curl -s https://codecov.io/bash) -f ./cobertura.xml -X coveragepy -X gcov -X xcode -t ${{ secrets.CODECOV_TOKEN }}

2
.gitignore поставляемый
Просмотреть файл

@ -42,3 +42,5 @@ Carthage/
.swiftpm
Package.resolved
*.generated.swift
/.env-vars

3
.slather.yml Normal file
Просмотреть файл

@ -0,0 +1,3 @@
coverage_service: cobertura_xml
xcodeproj: Nextcloud.xcodeproj
scheme: Nextcloud

Просмотреть файл

@ -42,6 +42,7 @@ disabled_rules:
excluded:
- Carthage
- Pods
- Tests
# iOS Files Quarantine

3
Gemfile Normal file
Просмотреть файл

@ -0,0 +1,3 @@
source 'https://rubygems.org'
gem 'slather'
gem 'xcpretty'

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -57,8 +57,52 @@
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "AF8ED1F82757821000B8DBC4"
BuildableName = "NextcloudTests.xctest"
BlueprintName = "NextcloudTests"
BuildableName = "NextcloudUnitTests.xctest"
BlueprintName = "NextcloudUnitTests"
ReferencedContainer = "container:Nextcloud.xcodeproj">
</BuildableReference>
</TestableReference>
<TestableReference
skipped = "NO"
parallelizable = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "C0046CD92A17B98400D87C9D"
BuildableName = "NextcloudUITests.xctest"
BlueprintName = "NextcloudUITests"
ReferencedContainer = "container:Nextcloud.xcodeproj">
</BuildableReference>
</TestableReference>
<TestableReference
skipped = "NO"
parallelizable = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "C04E2F1F2A17BB4D001BAD85"
BuildableName = "NextcloudIntegrationTests.xctest"
BlueprintName = "NextcloudIntegrationTests"
ReferencedContainer = "container:Nextcloud.xcodeproj">
</BuildableReference>
</TestableReference>
<TestableReference
skipped = "NO"
parallelizable = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "C04E2F2C2A17BB77001BAD85"
BuildableName = "NextcloudEndToEndTests.xctest"
BlueprintName = "NextcloudEndToEndTests"
ReferencedContainer = "container:Nextcloud.xcodeproj">
</BuildableReference>
</TestableReference>
<TestableReference
skipped = "NO"
parallelizable = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "F31F69412A2F6D4500162F76"
BuildableName = "NextcloudSnapshotTests.xctest"
BlueprintName = "NextcloudSnapshotTests"
ReferencedContainer = "container:Nextcloud.xcodeproj">
</BuildableReference>
</TestableReference>

Просмотреть файл

@ -26,7 +26,8 @@
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES"
shouldUseLaunchSchemeArgsEnv = "NO"
enableThreadSanitizer = "YES"
codeCoverageEnabled = "YES">
<MacroExpansion>
<BuildableReference
@ -43,8 +44,49 @@
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "AF8ED1F82757821000B8DBC4"
BuildableName = "NextcloudTests.xctest"
BlueprintName = "NextcloudTests"
BuildableName = "NextcloudUnitTests.xctest"
BlueprintName = "NextcloudUnitTests"
ReferencedContainer = "container:Nextcloud.xcodeproj">
</BuildableReference>
</TestableReference>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "C0046CD92A17B98400D87C9D"
BuildableName = "NextcloudUITests.xctest"
BlueprintName = "NextcloudUITests"
ReferencedContainer = "container:Nextcloud.xcodeproj">
</BuildableReference>
</TestableReference>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "C04E2F1F2A17BB4D001BAD85"
BuildableName = "NextcloudIntegrationTests.xctest"
BlueprintName = "NextcloudIntegrationTests"
ReferencedContainer = "container:Nextcloud.xcodeproj">
</BuildableReference>
</TestableReference>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "C04E2F2C2A17BB77001BAD85"
BuildableName = "NextcloudEndToEndTests.xctest"
BlueprintName = "NextcloudEndToEndTests"
ReferencedContainer = "container:Nextcloud.xcodeproj">
</BuildableReference>
</TestableReference>
<TestableReference
skipped = "NO"
parallelizable = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "F31F69412A2F6D4500162F76"
BuildableName = "NextcloudSnapshotTests.xctest"
BlueprintName = "NextcloudSnapshotTests"
ReferencedContainer = "container:Nextcloud.xcodeproj">
</BuildableReference>
</TestableReference>
@ -54,6 +96,7 @@
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
enableThreadSanitizer = "YES"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"

Просмотреть файл

@ -43,6 +43,50 @@
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
<TestableReference
skipped = "NO"
parallelizable = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "C0046CD92A17B98400D87C9D"
BuildableName = "NextcloudUITests.xctest"
BlueprintName = "NextcloudUITests"
ReferencedContainer = "container:Nextcloud.xcodeproj">
</BuildableReference>
</TestableReference>
<TestableReference
skipped = "NO"
parallelizable = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "C04E2F1F2A17BB4D001BAD85"
BuildableName = "NextcloudIntegrationTests.xctest"
BlueprintName = "NextcloudIntegrationTests"
ReferencedContainer = "container:Nextcloud.xcodeproj">
</BuildableReference>
</TestableReference>
<TestableReference
skipped = "NO"
parallelizable = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "C04E2F2C2A17BB77001BAD85"
BuildableName = "NextcloudEndToEndTests.xctest"
BlueprintName = "NextcloudEndToEndTests"
ReferencedContainer = "container:Nextcloud.xcodeproj">
</BuildableReference>
</TestableReference>
<TestableReference
skipped = "NO"
parallelizable = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "F31F69412A2F6D4500162F76"
BuildableName = "NextcloudSnapshotTests.xctest"
BlueprintName = "NextcloudSnapshotTests"
ReferencedContainer = "container:Nextcloud.xcodeproj">
</BuildableReference>
</TestableReference>
</Testables>
</TestAction>
<LaunchAction

Просмотреть файл

@ -57,8 +57,52 @@
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "AF8ED1F82757821000B8DBC4"
BuildableName = "NextcloudTests.xctest"
BlueprintName = "NextcloudTests"
BuildableName = "NextcloudUnitTests.xctest"
BlueprintName = "NextcloudUnitTests"
ReferencedContainer = "container:Nextcloud.xcodeproj">
</BuildableReference>
</TestableReference>
<TestableReference
skipped = "NO"
parallelizable = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "C0046CD92A17B98400D87C9D"
BuildableName = "NextcloudUITests.xctest"
BlueprintName = "NextcloudUITests"
ReferencedContainer = "container:Nextcloud.xcodeproj">
</BuildableReference>
</TestableReference>
<TestableReference
skipped = "NO"
parallelizable = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "C04E2F1F2A17BB4D001BAD85"
BuildableName = "NextcloudIntegrationTests.xctest"
BlueprintName = "NextcloudIntegrationTests"
ReferencedContainer = "container:Nextcloud.xcodeproj">
</BuildableReference>
</TestableReference>
<TestableReference
skipped = "NO"
parallelizable = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "C04E2F2C2A17BB77001BAD85"
BuildableName = "NextcloudEndToEndTests.xctest"
BlueprintName = "NextcloudEndToEndTests"
ReferencedContainer = "container:Nextcloud.xcodeproj">
</BuildableReference>
</TestableReference>
<TestableReference
skipped = "NO"
parallelizable = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "F31F69412A2F6D4500162F76"
BuildableName = "NextcloudSnapshotTests.xctest"
BlueprintName = "NextcloudSnapshotTests"
ReferencedContainer = "container:Nextcloud.xcodeproj">
</BuildableReference>
</TestableReference>

Просмотреть файл

@ -43,6 +43,50 @@
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
<TestableReference
skipped = "NO"
parallelizable = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "C0046CD92A17B98400D87C9D"
BuildableName = "NextcloudUITests.xctest"
BlueprintName = "NextcloudUITests"
ReferencedContainer = "container:Nextcloud.xcodeproj">
</BuildableReference>
</TestableReference>
<TestableReference
skipped = "NO"
parallelizable = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "C04E2F1F2A17BB4D001BAD85"
BuildableName = "NextcloudIntegrationTests.xctest"
BlueprintName = "NextcloudIntegrationTests"
ReferencedContainer = "container:Nextcloud.xcodeproj">
</BuildableReference>
</TestableReference>
<TestableReference
skipped = "NO"
parallelizable = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "C04E2F2C2A17BB77001BAD85"
BuildableName = "NextcloudEndToEndTests.xctest"
BlueprintName = "NextcloudEndToEndTests"
ReferencedContainer = "container:Nextcloud.xcodeproj">
</BuildableReference>
</TestableReference>
<TestableReference
skipped = "NO"
parallelizable = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "F31F69412A2F6D4500162F76"
BuildableName = "NextcloudSnapshotTests.xctest"
BlueprintName = "NextcloudSnapshotTests"
ReferencedContainer = "container:Nextcloud.xcodeproj">
</BuildableReference>
</TestableReference>
</Testables>
</TestAction>
<LaunchAction

Просмотреть файл

@ -79,3 +79,53 @@ If you need assistance or want to ask a question about the iOS app, you are welc
Do you want to try the latest version in development of Nextcloud iOS ? Simple, follow this simple step
[Apple TestFlight](https://testflight.apple.com/join/RXEJbWj9)
## Testing
#### Note: If a Unit or Integration test exclusively uses and tests NextcloudKit functions and components, then write that test in the NextcloudKit repo. NextcloudKit is used in many other repos as an API, and it's better if such tests are located there.
### Unit tests:
There are currently no preresquites for unit testing that need to be done. Mock everything that's not needed.
### Integration tests:
To run integration tests, we need a docker instance of a Nextcloud test server.
The CI does all this automatically, but to do it manually:
1. Run `docker run --rm -d -p 8080:80 ghcr.io/juliushaertl/nextcloud-dev-php80:latest` to spin up a docker container of the Nextcloud test server.
2. Log in on the test server and generate an app password for device. There are a couple test accounts, but `admin` as username and password works best.
3. Build the iOS project once. This will generate an `.env-vars` file in the root directory. It contains env vars that the project will use for testing.
4. Provide proper values for the env vars inside the file. Here is an example:
```
export TEST_SERVER_URL=http://localhost:8080
export TEST_USER=nextcloud
export TEST_PASSWORD=FAeSR-6Jk7s-DzLny-CCQHL-f49BP
```
5. Build the iOS project again. If all the values are set correctly you will see a generated file called `EnvVars.generated.swift`. It contains the env vars as Swift fields that can be easily used in code:
```
/**
This is generated from the .env-vars file in the root directory. If there is an environment variable here that is needed and not filled, please look into this file.
*/
public struct EnvVars {
static let testUser = "nextcloud"
static let testPassword = "FAeSR-6Jk7s-DzLny-CCQHL-f49BP"
static let testServerUrl = "http://localhost:8080"
}
```
6. You can now run the integration tests. They will use the env vars to connect to the test server to do the testing.
### UI tests
UI tests also use the docker server, but besides that there is nothing else you need to do.
### Snapshot tests
Snapshot tests are made via this library: https://github.com/pointfreeco/swift-snapshot-testing and these 2 extensions:
1. https://github.com/doordash-oss/swiftui-preview-snapshots - for creating SwiftUI snapshot tests via previews.
2. https://github.com/alexey1312/SnapshotTestingHEIC - makes snapshot images HEIC instead of PNGs for much reduced size.
Snapshot tests are a great way to test if UI elements are consistent with designs and don't break with new commits, but they can be very finicky and the smallest change can cause them to fail. Keep in mind:
- For SwiftUI snapshot tests, It's always a good idea to utilize previews, as they do not depend on device/app state and it has less chances to fail due to wrong state.
- For UIKit snapshot tests, try to include mock dependencies to always make sure the UI is rendered the same way. Even a text change can cause the tests to fail.

18
Sourcery/EnvVars.stencil Normal file
Просмотреть файл

@ -0,0 +1,18 @@
//
// EnvVars.stencil.swift
// NextcloudIntegrationTests
//
// Created by Milen on 31.05.23.
// Copyright © 2023 Marino Faggiana. All rights reserved.
//
import Foundation
/**
This is generated from the .env-vars file in the root directory. If there is an environment variable here that is needed and not filled, please look into this file.
*/
public struct EnvVars {
static let testUser = "{{ argument.TEST_USER }}"
static let testAppPassword = "{{ argument.TEST_APP_PASSWORD }}"
static let testServerUrl = "{{ argument.TEST_SERVER_URL }}"
}

Двоичные данные
Sourcery/bin/sourcery Executable file

Двоичный файл не отображается.

Просмотреть файл

@ -0,0 +1,81 @@
//
// NextcloudIntegrationTests.swift
// NextcloudIntegrationTests
//
// Created by Milen Pivchev on 5/19/23.
// Copyright © 2023 Marino Faggiana. All rights reserved.
//
import XCTest
import NextcloudKit
@testable import Nextcloud
final class FilesIntegrationTests: XCTestCase {
private let baseUrl = EnvVars.testServerUrl
private let user = EnvVars.testUser
private let userId = EnvVars.testUser
private let password = EnvVars.testAppPassword
private lazy var account = "\(userId) \(baseUrl)"
private let appDelegate = (UIApplication.shared.delegate as? AppDelegate)!
override func setUp() {
appDelegate.deleteAllAccounts()
}
func test_createReadDeleteFolder_withProperParams_shouldCreateReadDeleteFolder() throws {
let expectation = expectation(description: "Should finish last callback")
let folderName = "TestFolder10"
let serverUrl = "\(baseUrl)/remote.php/dav/files/\(userId)"
let serverUrlFileName = "\(serverUrl)/\(folderName)"
NextcloudKit.shared.setup(account: account, user: user, userId: userId, password: password, urlBase: baseUrl)
// Test creating folder
NCNetworking.shared.createFolder(fileName: folderName, serverUrl: serverUrl, account: account, urlBase: baseUrl, userId: userId, withPush: true) { error in
XCTAssertEqual(NKError.success.errorCode, error.errorCode)
XCTAssertEqual(NKError.success.errorDescription, error.errorDescription)
Thread.sleep(forTimeInterval: 0.2)
// Test reading folder, should exist
NCNetworking.shared.readFolder(serverUrl: serverUrlFileName, account: self.user) { account, metadataFolder, metadatas, metadatasUpdate, metadatasLocalUpdate, metadatasDelete, error in
XCTAssertEqual(self.account, account)
XCTAssertEqual(NKError.success.errorCode, error.errorCode)
XCTAssertEqual(NKError.success.errorDescription, error.errorDescription)
XCTAssertEqual(metadataFolder?.fileName, folderName)
// Check Realm directory, should exist
let directory = NCManageDatabase.shared.getTableDirectory(predicate: NSPredicate(format: "serverUrl == %@", serverUrlFileName))
XCTAssertNotNil(directory)
Thread.sleep(forTimeInterval: 0.2)
Task {
// Test deleting folder
await _ = NCNetworking.shared.deleteMetadata(metadataFolder!, onlyLocalCache: false)
XCTAssertEqual(NKError.success.errorCode, error.errorCode)
XCTAssertEqual(NKError.success.errorDescription, error.errorDescription)
try await Task.sleep(for: .milliseconds(200))
// Test reading folder, should NOT exist
NCNetworking.shared.readFolder(serverUrl: serverUrlFileName, account: self.user) { account, metadataFolder, metadatas, metadatasUpdate, metadatasLocalUpdate, metadatasDelete, error in
defer { expectation.fulfill() }
XCTAssertEqual(404, error.errorCode)
XCTAssertNil(metadataFolder?.fileName)
// Check Realm directory, should NOT exist
let directory = NCManageDatabase.shared.getTableDirectory(predicate: NSPredicate(format: "serverUrl == %@", serverUrlFileName))
XCTAssertNil(directory)
}
}
}
}
waitForExpectations(timeout: 100)
}
}

Просмотреть файл

@ -0,0 +1,83 @@
//
// NextcloudIntegrationTests.swift
// NextcloudIntegrationTests
//
// Created by Milen Pivchev on 5/19/23.
// Copyright © 2023 Marino Faggiana. All rights reserved.
//
import XCTest
import NextcloudKit
@testable import Nextcloud
final class LoginIntegrationTests: XCTestCase {
private let baseUrl = EnvVars.testServerUrl
private let user = EnvVars.testUser
private let userId = EnvVars.testUser
private let password = EnvVars.testAppPassword
private lazy var account = "\(userId) \(baseUrl)"
private let appDelegate = (UIApplication.shared.delegate as? AppDelegate)!
override func setUp() {
appDelegate.deleteAllAccounts()
}
func test_createReadDeleteFolder_withProperParams_shouldCreateReadDeleteFolder() throws {
let expectation = expectation(description: "Should finish last callback")
let folderName = "TestFolder10"
let serverUrl = "\(baseUrl)/remote.php/dav/files/\(userId)"
let serverUrlFileName = "\(serverUrl)/\(folderName)"
NextcloudKit.shared.setup(account: account, user: user, userId: userId, password: password, urlBase: baseUrl)
// Test creating folder
NCNetworking.shared.createFolder(fileName: folderName, serverUrl: serverUrl, account: account, urlBase: baseUrl, userId: userId, withPush: true) { error in
XCTAssertEqual(NKError.success.errorCode, error.errorCode)
XCTAssertEqual(NKError.success.errorDescription, error.errorDescription)
Thread.sleep(forTimeInterval: 0.2)
// Test reading folder, should exist
NCNetworking.shared.readFolder(serverUrl: serverUrlFileName, account: self.user) { account, metadataFolder, metadatas, metadatasUpdate, metadatasLocalUpdate, metadatasDelete, error in
XCTAssertEqual(self.account, account)
XCTAssertEqual(NKError.success.errorCode, error.errorCode)
XCTAssertEqual(NKError.success.errorDescription, error.errorDescription)
XCTAssertEqual(metadataFolder?.fileName, folderName)
// Check Realm directory, should exist
let directory = NCManageDatabase.shared.getTableDirectory(predicate: NSPredicate(format: "serverUrl == %@", serverUrlFileName))
XCTAssertNotNil(directory)
Thread.sleep(forTimeInterval: 0.2)
Task {
// Test deleting folder
await _ = NCNetworking.shared.deleteMetadata(metadataFolder!, onlyLocalCache: false)
XCTAssertEqual(NKError.success.errorCode, error.errorCode)
XCTAssertEqual(NKError.success.errorDescription, error.errorDescription)
try await Task.sleep(for: .milliseconds(200))
// Test reading folder, should NOT exist
NCNetworking.shared.readFolder(serverUrl: serverUrlFileName, account: self.user) { account, metadataFolder, metadatas, metadatasUpdate, metadatasLocalUpdate, metadatasDelete, error in
defer { expectation.fulfill() }
XCTAssertEqual(404, error.errorCode)
XCTAssertNil(metadataFolder?.fileName)
// Check Realm directory, should NOT exist
let directory = NCManageDatabase.shared.getTableDirectory(predicate: NSPredicate(format: "serverUrl == %@", serverUrlFileName))
XCTAssertNil(directory)
}
}
}
}
waitForExpectations(timeout: 100)
}
}

Просмотреть файл

@ -0,0 +1,18 @@
//
// SwiftUIView+Extensions.swift
// Nextcloud
//
// Created by Milen on 06.06.23.
// Copyright © 2023 Marino Faggiana. All rights reserved.
//
import Foundation
import SwiftUI
extension SwiftUI.View {
func toVC() -> UIViewController {
let vc = UIHostingController (rootView: self)
vc.view.frame = UIScreen.main.bounds
return vc
}
}

Просмотреть файл

@ -0,0 +1,24 @@
//
// NextcloudSnapshotTests.swift
// NextcloudSnapshotTests
//
// Created by Milen on 06.06.23.
// Copyright © 2023 Marino Faggiana. All rights reserved.
//
import XCTest
import SnapshotTesting
import SnapshotTestingHEIC
import PreviewSnapshotsTesting
import SwiftUI
@testable import Nextcloud
final class NextcloudSnapshotTests: XCTestCase {
func test_HUDView() {
HUDView_Previews.snapshots.assertSnapshots(as: .imageHEIC)
}
func test_CapalitiesView() {
NCCapabilitiesView_Previews.snapshots.assertSnapshots(as: .imageHEIC)
}
}

Двоичный файл не отображается.

Двоичный файл не отображается.

Просмотреть файл

@ -0,0 +1,35 @@
//
// BaseUIXCTestCase.swift
// NextcloudUITests
//
// Created by Milen on 20.06.23.
// Copyright © 2023 Marino Faggiana. All rights reserved.
//
import XCTest
class BaseUIXCTestCase: XCTestCase {
let timeoutSeconds: Double = 100
override final class var runsForEachTargetApplicationUIConfiguration: Bool {
false
}
internal func waitForEnabled(object: Any?) {
let predicate = NSPredicate(format: "enabled == true")
expectation(for: predicate, evaluatedWith: object, handler: nil)
waitForExpectations(timeout: timeoutSeconds, handler: nil)
}
internal func waitForHittable(object: Any?) {
let predicate = NSPredicate(format: "hittable == true")
expectation(for: predicate, evaluatedWith: object, handler: nil)
waitForExpectations(timeout: timeoutSeconds, handler: nil)
}
internal func waitForEnabledAndHittable(object: Any?) {
waitForEnabled(object: object)
waitForHittable(object: object)
}
}

Просмотреть файл

@ -0,0 +1,64 @@
//
// NextcloudUITests.swift
// NextcloudUITests
//
// Created by Milen Pivchev on 5/19/23.
// Copyright © 2023 Marino Faggiana. All rights reserved.
//
import XCTest
final class LoginUITests: BaseUIXCTestCase {
private let baseUrl = EnvVars.testServerUrl
private let user = EnvVars.testUser
private let userId = EnvVars.testUser
private let password = EnvVars.testAppPassword
private lazy var account = "\(userId) \(baseUrl)"
let app = XCUIApplication()
override func setUp() {
app.launchArguments += ["UI_TESTING"]
}
func test_logIn_withProperParams_shouldLogInAndGoToHomeScreen() throws {
app.launch()
let loginButton = app.buttons["Log in"]
XCTAssert(loginButton.waitForExistence(timeout: timeoutSeconds))
loginButton.tap()
let serverAddressHttpsTextField = app.textFields["Server address https:// …"]
serverAddressHttpsTextField.tap()
serverAddressHttpsTextField.typeText(baseUrl)
let button = app.windows.children(matching: .other).element.children(matching: .other).element.children(matching: .other).element.children(matching: .other).element.children(matching: .other).element.children(matching: .other).element.children(matching: .button).element(boundBy: 0)
button.tap()
let webViewsQuery = app.webViews.webViews.webViews
let loginButton2 = webViewsQuery/*@START_MENU_TOKEN@*/.buttons["Log in"]/*[[".otherElements.matching(identifier: \"Nextcloud\")",".otherElements[\"main\"].buttons[\"Log in\"]",".buttons[\"Log in\"]"],[[[-1,2],[-1,1],[-1,0,1]],[[-1,2],[-1,1]]],[0]]@END_MENU_TOKEN@*/
XCTAssert(loginButton2.waitForExistence(timeout: timeoutSeconds))
waitForEnabledAndHittable(object: loginButton2)
loginButton2.tap()
let element = webViewsQuery/*@START_MENU_TOKEN@*/.otherElements["main"]/*[[".otherElements[\"Login Nextcloud\"].otherElements[\"main\"]",".otherElements[\"main\"]"],[[[-1,1],[-1,0]]],[0]]@END_MENU_TOKEN@*/.children(matching: .other).element(boundBy: 1)
let usernameTextField = element.children(matching: .other).element(boundBy: 2).children(matching: .textField).element
XCTAssert(usernameTextField.waitForExistence(timeout: timeoutSeconds))
usernameTextField.tap()
usernameTextField.typeText(user)
let passwordTextField = element.children(matching: .other).element(boundBy: 4).children(matching: .secureTextField).element
passwordTextField.tap()
passwordTextField.typeText(user)
let loginButton3 = webViewsQuery/*@START_MENU_TOKEN@*/.buttons["Log in"]/*[[".otherElements[\"Login Nextcloud\"]",".otherElements[\"main\"].buttons[\"Log in\"]",".buttons[\"Log in\"]"],[[[-1,2],[-1,1],[-1,0,1]],[[-1,2],[-1,1]]],[0]]@END_MENU_TOKEN@*/
XCTAssert(loginButton3.waitForExistence(timeout: timeoutSeconds))
loginButton3.tap()
let grantAccessButton = webViewsQuery/*@START_MENU_TOKEN@*/.buttons["Grant access"]/*[[".otherElements.matching(identifier: \"Nextcloud\")",".otherElements[\"main\"].buttons[\"Grant access\"]",".buttons[\"Grant access\"]"],[[[-1,2],[-1,1],[-1,0,1]],[[-1,2],[-1,1]]],[0]]@END_MENU_TOKEN@*/
XCTAssert(grantAccessButton.waitForExistence(timeout: timeoutSeconds))
waitForEnabledAndHittable(object: grantAccessButton)
grantAccessButton.tap()
// Check if we are in the home screen
XCTAssert(app.navigationBars["Nextcloud"].waitForExistence(timeout: timeoutSeconds))
XCTAssert(app.tabBars["Tab Bar"].waitForExistence(timeout: timeoutSeconds))
}
}

Просмотреть файл

@ -8,7 +8,7 @@
import XCTest
class NextcloudTests: XCTestCase {
class NextcloudUnitTests: XCTestCase {
override func setUpWithError() throws {
// Put setup code here. This method is called before the invocation of each test method in the class.

49
create-docker-test-server.sh Executable file
Просмотреть файл

@ -0,0 +1,49 @@
#!/usr/bin/env bash
#This script creates a testable Docker enviroment of the Nextcloud server, and is used by the CI for tests.
container_name="nextcloud_test"
port=8080
server_url="http://localhost:${port}"
user="admin"
docker run --rm -d --name $container_name -p $port:80 ghcr.io/juliushaertl/nextcloud-dev-php80:latest
timeout=300
elapsed=0
echo "Waiting for server..."
sleep 2
while true; do
content=$(curl -s $server_url/status.php)
if [[ $content == *"installed\":true"* ]]; then
break
fi
elapsed=$((elapsed + 1))
if [ $elapsed -ge $timeout ]; then
echo "No success after $timeout seconds."
exit 1
fi
sleep 1
done
echo "Server is installed."
echo "Exporting env vars..."
sleep 2
password=$(docker exec -e NC_PASS=$user $container_name sudo -E -u www-data php /var/www/html/occ user:add-app-password $user --password-from-env | tail -1)
export TEST_APP_PASSWORD=$password
export TEST_SERVER_URL=$server_url
export TEST_USER=$user
echo "TEST_SERVER_URL: ${TEST_SERVER_URL}"
echo "TEST_USER: ${TEST_USER}"
echo "TEST_APP_PASSWORD: ${TEST_APP_PASSWORD}"
echo "Env vars exported."

Просмотреть файл

@ -59,7 +59,16 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
private var privacyProtectionWindow: UIWindow?
var isUiTestingEnabled: Bool {
get {
return ProcessInfo.processInfo.arguments.contains("UI_TESTING")
}
}
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
if isUiTestingEnabled {
deleteAllAccounts()
}
NCSettingsBundleHelper.checkAndExecuteSettings(delay: 0)
@ -625,6 +634,13 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
}
}
@objc func deleteAllAccounts() {
let accounts = NCManageDatabase.shared.getAccounts()
accounts?.forEach({ account in
deleteAccount(account, wipe: true)
})
}
@objc func changeAccount(_ account: String) {
NCManageDatabase.shared.setAccountActive(account)

Просмотреть файл

@ -8,6 +8,7 @@
import SwiftUI
import NextcloudKit
import PreviewSnapshots
@objc class NCHostingCapabilitiesView: NSObject {
@ -35,13 +36,6 @@ class NCCapabilitiesViewOO: ObservableObject {
@Published var homeServer = ""
init() {
if ProcessInfo.processInfo.environment["XCODE_RUNNING_FOR_PREVIEWS"] == "1" {
capabililies = [Capability(text: "Collabora", image: UIImage(named: "collabora")!, resize: true, available: true),
Capability(text: "XXX site", image: UIImage(systemName: "lock.shield")!, resize: false, available: false)
]
homeServer = "https://cloud.nextcloud.com/remote.php.dav/files/marino/"
} else {
guard let activeAccount = NCManageDatabase.shared.getActiveAccount() else { return }
var textEditor = false
var onlyofficeEditors = false
@ -100,7 +94,6 @@ class NCCapabilitiesViewOO: ObservableObject {
homeServer = NCUtilityFileSystem.shared.getHomeServer(urlBase: activeAccount.urlBase, userId: activeAccount.userId) + "/"
}
}
}
struct NCCapabilitiesView: View {
@ -175,6 +168,26 @@ struct NCCapabilitiesView: View {
struct NCCapabilitiesView_Previews: PreviewProvider {
static var previews: some View {
NCCapabilitiesView(capabilitiesStatus: NCCapabilitiesViewOO())
snapshots.previews.previewLayout(.device)
}
static var snapshots: PreviewSnapshots<String> {
PreviewSnapshots(
configurations: [
.init(name: NCGlobal.shared.defaultSnapshotConfiguration, state: "")
],
configure: { _ in
NCCapabilitiesView(capabilitiesStatus: getCapabilitiesViewOOForPreview()).padding(.top, 20).frameForPreview()
})
}
}
func getCapabilitiesViewOOForPreview() -> NCCapabilitiesViewOO {
let capabilitiesViewOO = NCCapabilitiesViewOO()
capabilitiesViewOO.capabililies = [
NCCapabilitiesViewOO.Capability(text: "Collabora", image: UIImage(named: "collabora")!, resize: true, available: true),
NCCapabilitiesViewOO.Capability(text: "XXX site", image: UIImage(systemName: "lock.shield")!, resize: false, available: false)
]
capabilitiesViewOO.homeServer = "https://cloud.nextcloud.com/remote.php.dav/files/marino/"
return capabilitiesViewOO
}

Просмотреть файл

@ -24,8 +24,13 @@
import SwiftUI
extension View {
func complexModifier<V: View>(@ViewBuilder _ closure: (Self) -> V) -> some View {
closure(self)
}
/// Use this on preview views that are used in snapshot testing. It prevents the snapashot library from complaining that the view has a size of 0
/// Check: https://github.com/pointfreeco/swift-snapshot-testing/issues/738
func frameForPreview() -> some View {
return frame(width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height)
}
}

Просмотреть файл

@ -22,6 +22,7 @@
//
import SwiftUI
import PreviewSnapshots
struct HUDView: View {
@ -93,6 +94,16 @@ struct ContentView: View {
struct HUDView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
snapshots.previews.previewLayout(.sizeThatFits)
}
static var snapshots: PreviewSnapshots<String> {
PreviewSnapshots(
configurations: [
.init(name: NCGlobal.shared.defaultSnapshotConfiguration, state: "")
],
configure: { _ in
ContentView().frameForPreview()
})
}
}

Просмотреть файл

@ -441,4 +441,7 @@ class NCGlobal: NSObject {
@objc var capabilityUserStatusEnabled: Bool = false
var capabilityExternalSites: Bool = false
var capabilityGroupfoldersEnabled: Bool = false // NC27
// SNAPSHOT PREVIEW
let defaultSnapshotConfiguration = "DefaultPreviewConfiguration"
}