diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml new file mode 100644 index 0000000..3c77bd8 --- /dev/null +++ b/.github/workflows/tests.yml @@ -0,0 +1,47 @@ +name: iOS Tests + +on: + push: + branches: [ main ] + pull_request: + branches: [ main ] + +jobs: + build: + name: Build and Test default scheme using any available iPhone simulator + runs-on: macos-latest + + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Set Default Scheme + run: | + scheme_list=$(xcodebuild -list -json | tr -d "\n") + default=$(echo $scheme_list | ruby -e "require 'json'; puts JSON.parse(STDIN.gets)['project']['targets'][0]") + echo $default | cat >default + echo Using default scheme: $default + + - name: Build + env: + scheme: ${{ 'default' }} + platform: ${{ 'iOS Simulator' }} + run: | + # xcrun xctrace returns via stderr, not the expected stdout (see https://developer.apple.com/forums/thread/663959) + device=`xcrun xctrace list devices 2>&1 | grep -oE 'iPhone.*?[^\(]+' | head -1 | awk '{$1=$1;print}' | sed -e "s/ Simulator$//"` + if [ $scheme = default ]; then scheme=$(cat default); fi + if [ "`ls -A | grep -i \\.xcworkspace\$`" ]; then filetype_parameter="workspace" && file_to_build="`ls -A | grep -i \\.xcworkspace\$`"; else filetype_parameter="project" && file_to_build="`ls -A | grep -i \\.xcodeproj\$`"; fi + file_to_build=`echo $file_to_build | awk '{$1=$1;print}'` + xcodebuild build-for-testing -scheme "$scheme" -"$filetype_parameter" "$file_to_build" -destination "platform=$platform,name=$device" + + - name: Test + env: + scheme: ${{ 'default' }} + platform: ${{ 'iOS Simulator' }} + run: | + # xcrun xctrace returns via stderr, not the expected stdout (see https://developer.apple.com/forums/thread/663959) + device=`xcrun xctrace list devices 2>&1 | grep -oE 'iPhone.*?[^\(]+' | head -1 | awk '{$1=$1;print}' | sed -e "s/ Simulator$//"` + if [ $scheme = default ]; then scheme=$(cat default); fi + if [ "`ls -A | grep -i \\.xcworkspace\$`" ]; then filetype_parameter="workspace" && file_to_build="`ls -A | grep -i \\.xcworkspace\$`"; else filetype_parameter="project" && file_to_build="`ls -A | grep -i \\.xcodeproj\$`"; fi + file_to_build=`echo $file_to_build | awk '{$1=$1;print}'` + xcodebuild test-without-building -scheme "$scheme" -"$filetype_parameter" "$file_to_build" -destination "platform=$platform,name=$device" diff --git a/Coinly/TEST/AccountTests.swift b/Coinly/TEST/AccountTests.swift new file mode 100644 index 0000000..51d040d --- /dev/null +++ b/Coinly/TEST/AccountTests.swift @@ -0,0 +1,151 @@ +import XCTest +@testable import Coinly + +class AccountTests: XCTestCase { + var sut: AccountModel! // system under test + + override func setUp() { + super.setUp() + // Создаем тестовый аккаунт перед каждым тестом + sut = AccountModel( + name: "Test Account", + type: .wallet, + currency: .usd, + balance: 1000 + ) + } + + override func tearDown() { + sut = nil + super.tearDown() + } + + func testAccountCreation() { + XCTAssertNotNil(sut) + XCTAssertEqual(sut.name, "Test Account") + XCTAssertEqual(sut.type, .wallet) + XCTAssertEqual(sut.currency, .usd) + XCTAssertEqual(sut.balance, 1000) + XCTAssertTrue(sut.isActive) + } + + func testCreditCardOperations() { + // Создаем кредитную карту для теста + let creditCard = AccountModel( + name: "Test Credit Card", + type: .creditCard, + currency: .usd, + balance: 0, + creditLimit: 1000, + interestRate: 19.99 + ) + + // Тестируем добавление покупки + var card = creditCard + XCTAssertTrue(card.addPurchase(500)) + XCTAssertEqual(card.balance, 500) + + // Тестируем превышение лимита + XCTAssertFalse(card.addPurchase(600)) + XCTAssertEqual(card.balance, 500) + + // Тестируем оплату + XCTAssertTrue(card.makePayment(200)) + XCTAssertEqual(card.balance, 300) + } + + func testAvailableCredit() { + let creditCard = AccountModel( + name: "Test Credit Card", + type: .creditCard, + currency: .usd, + balance: 500, + creditLimit: 1000 + ) + + XCTAssertEqual(creditCard.availableCredit, 500) + } +} + +class TransactionTests: XCTestCase { + func testTransactionCreation() { + let transaction = TransactionModel( + amount: 100, + date: Date(), + type: .expense, + category: "Food", + note: "Lunch", + originalCurrency: .usd + ) + + XCTAssertEqual(transaction.amount, 100) + XCTAssertEqual(transaction.type, .expense) + XCTAssertEqual(transaction.category, "Food") + XCTAssertEqual(transaction.note, "Lunch") + XCTAssertEqual(transaction.originalCurrency, .usd) + } + + func testCurrencyConversion() { + let transaction = TransactionModel( + amount: 100, + date: Date(), + type: .expense, + category: "Food", + originalCurrency: .usd + ) + + let settings = AppSettings.shared + settings.currency = .eur + + let convertedAmount = transaction.amountInCurrentCurrency() + XCTAssertNotEqual(convertedAmount, 100) + } +} + +class AccountsStoreTests: XCTestCase { + var store: AccountsStore! + + override func setUp() { + super.setUp() + store = AccountsStore() + } + + override func tearDown() { + store = nil + super.tearDown() + } + + func testAddAccount() { + let initialCount = store.accounts.count + + let newAccount = AccountModel( + name: "Test Account", + type: .wallet, + currency: .usd, + balance: 1000 + ) + + store.addAccount(newAccount) + + XCTAssertEqual(store.accounts.count, initialCount + 1) + XCTAssertEqual(store.accounts.last?.name, "Test Account") + } + + func testDeleteAccount() { + let account = AccountModel( + name: "Test Account", + type: .wallet, + currency: .usd, + balance: 1000 + ) + + store.addAccount(account) + let initialCount = store.accounts.count + + if let index = store.accounts.firstIndex(where: { $0.id == account.id }) { + store.deleteAccount(at: IndexSet([index])) + } + + XCTAssertEqual(store.accounts.count, initialCount - 1) + } +} \ No newline at end of file diff --git a/Coinly/TEST/CoinlyUITests.swift b/Coinly/TEST/CoinlyUITests.swift new file mode 100644 index 0000000..5bb426a --- /dev/null +++ b/Coinly/TEST/CoinlyUITests.swift @@ -0,0 +1,67 @@ +import XCTest + +class CoinlyUITests: XCTestCase { + var app: XCUIApplication! + + override func setUp() { + super.setUp() + continueAfterFailure = false + app = XCUIApplication() + app.launch() + } + + func testAddNewTransaction() { + // Нажимаем на вкладку Transactions + app.tabBars.buttons["Transactions"].tap() + + // Нажимаем кнопку добавления + app.navigationBars.buttons["Add"].tap() + + // Вводим сумму + let amountField = app.textFields["Amount"] + amountField.tap() + amountField.typeText("100") + + // Выбираем тип транзакции + app.segmentedControls.buttons["Expense"].tap() + + // Выбираем категорию + app.buttons["Category"].tap() + app.buttons["Food"].tap() + + // Сохраняем транзакцию + app.navigationBars.buttons["Add"].tap() + + // Проверяем, что транзакция появилась в списке + XCTAssertTrue(app.staticTexts["$100.00"].exists) + } + + func testAddNewAccount() { + // Нажимаем на вкладку Dashboard + app.tabBars.buttons["Dashboard"].tap() + + // Открываем список счетов + app.buttons["Show All Accounts"].tap() + + // Нажимаем кнопку добавления + app.navigationBars.buttons["Add"].tap() + + // Выбираем тип счета + app.buttons["Wallet"].tap() + + // Заполняем данные счета + let nameField = app.textFields["Account Name"] + nameField.tap() + nameField.typeText("Test Wallet") + + let balanceField = app.textFields["Balance"] + balanceField.tap() + balanceField.typeText("1000") + + // Сохраняем счет + app.navigationBars.buttons["Add"].tap() + + // Проверяем, что счет появился в списке + XCTAssertTrue(app.staticTexts["Test Wallet"].exists) + } +} \ No newline at end of file