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

[#19] [Integrate] As a user, I can see Survey selection page #102

Merged
merged 6 commits into from
Jan 13, 2023
Merged
Show file tree
Hide file tree
Changes from 5 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
4 changes: 4 additions & 0 deletions iosApp/Survey.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@
09CE773328E2ED2300EAA9EE /* KoinApplication.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09CE773028E2ED2300EAA9EE /* KoinApplication.swift */; };
09CE773428E2ED2300EAA9EE /* Typealias+Koin.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09CE773128E2ED2300EAA9EE /* Typealias+Koin.swift */; };
09D09DB92963EC39009F88AF /* Image+Url.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09D09DB82963EC39009F88AF /* Image+Url.swift */; };
09D09DB2295C56A1009F88AF /* SurveySelectionSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09D09DB1295C56A1009F88AF /* SurveySelectionSpec.swift */; };
09E6ABF32951D105007F1EE3 /* KIF+Swift.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09E6ABF12951D103007F1EE3 /* KIF+Swift.swift */; };
09E6ABFD2951D32F007F1EE3 /* ViewId.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09636B1428D8148C00A5CB97 /* ViewId.swift */; };
09E6ABFE2951D333007F1EE3 /* ViewId+General.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09636B2F28D8267D00A5CB97 /* ViewId+General.swift */; };
Expand Down Expand Up @@ -274,6 +275,7 @@
09CE773028E2ED2300EAA9EE /* KoinApplication.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KoinApplication.swift; sourceTree = "<group>"; };
09CE773128E2ED2300EAA9EE /* Typealias+Koin.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Typealias+Koin.swift"; sourceTree = "<group>"; };
09D09DB82963EC39009F88AF /* Image+Url.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Image+Url.swift"; sourceTree = "<group>"; };
09D09DB1295C56A1009F88AF /* SurveySelectionSpec.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SurveySelectionSpec.swift; sourceTree = "<group>"; };
09E6ABE42951CF3E007F1EE3 /* SurveyKIFUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = SurveyKIFUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
09E6ABF12951D103007F1EE3 /* KIF+Swift.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "KIF+Swift.swift"; sourceTree = "<group>"; };
09E6AC0B2951D5DD007F1EE3 /* AccountSpec.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountSpec.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -888,6 +890,7 @@
isa = PBXGroup;
children = (
09E6AC0B2951D5DD007F1EE3 /* AccountSpec.swift */,
09D09DB1295C56A1009F88AF /* SurveySelectionSpec.swift */,
);
path = Module;
sourceTree = "<group>";
Expand Down Expand Up @@ -1774,6 +1777,7 @@
09E6ABFE2951D333007F1EE3 /* ViewId+General.swift in Sources */,
09E6AC012951D333007F1EE3 /* ViewId+Login.swift in Sources */,
09E6ABFF2951D333007F1EE3 /* ViewId+SurveySelection.swift in Sources */,
09D09DB2295C56A1009F88AF /* SurveySelectionSpec.swift in Sources */,
09E6AC042951D333007F1EE3 /* ViewId+Splash.swift in Sources */,
09E6ABF32951D105007F1EE3 /* KIF+Swift.swift in Sources */,
09E6ABFD2951D32F007F1EE3 /* ViewId.swift in Sources */,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,19 +43,19 @@
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "E7449C1BBC882ADE61DB38AC"
BuildableName = "SurveyUITests.xctest"
BlueprintName = "SurveyUITests"
BlueprintIdentifier = "09E6ABE32951CF3E007F1EE3"
BuildableName = "SurveyKIFUITests.xctest"
BlueprintName = "SurveyKIFUITests"
ReferencedContainer = "container:Survey.xcodeproj">
</BuildableReference>
</TestableReference>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "09E6ABE32951CF3E007F1EE3"
BuildableName = "SurveyKIFUITests.xctest"
BlueprintName = "SurveyKIFUITests"
BlueprintIdentifier = "E7449C1BBC882ADE61DB38AC"
BuildableName = "SurveyUITests.xctest"
BlueprintName = "SurveyUITests"
ReferencedContainer = "container:Survey.xcodeproj">
</BuildableReference>
</TestableReference>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ extension SurveySelectionView {

@Published private(set) var viewState = SurveySelectionViewState()
@Published var showingLoading = false
@Published private(set) var surveys = [SurveyUiModel]()

private var cancellables = Set<AnyCancellable>()

Expand All @@ -42,9 +43,14 @@ extension SurveySelectionView {
viewModel.fetch()
}

func checkFetchMore(index: Int) {
viewModel.checkFetchMore(itemIndex: Int32(index))
}

private func updateStates(_ state: SurveySelectionViewState) {
viewState = state
showingLoading = state.isLoading
surveys = state.surveys
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,27 +16,6 @@ struct SurveySelectionView: View {
@Binding var isShowingAccountView: Bool

@State private var currentPage = 0
// TODO: Replace Example data
@State private var surveys: [SurveyUiModel] = [
SurveyUiModel(
id: "1",
imageUrl: "https://dhdbhh0jsld0o.cloudfront.net/m/1ea51560991bcb7d00d0_",
title: "Scarlett Bangkok",
description: "We'd love ot hear from you!"
),
SurveyUiModel(
id: "2",
imageUrl: "https://dhdbhh0jsld0o.cloudfront.net/m/287db81c5e4242412cc0_",
title: "ibis Bangkok Riverside",
description: "We'd love ot hear from you!"
),
SurveyUiModel(
id: "3",
imageUrl: "https://dhdbhh0jsld0o.cloudfront.net/m/1ea51560991bcb7d00d0_",
title: "Scarlett Bangkok",
description: "We'd love ot hear from you!"
)
]

var body: some View {
ZStack {
Expand All @@ -63,17 +42,19 @@ struct SurveySelectionView: View {
nextView: { item in
AnyView(SurveyItemView(survey: item))
},
items: surveys
items: dataSource.surveys
)
.onChange(of: currentPage) { newValue in
// TODO: Update viewModel to new index
print(newValue)
.onChange(of: currentPage) { index in
dataSource.checkFetchMore(index: index)
}
VStack(alignment: .leading) {
Spacer()
HStack(alignment: .bottom) {
PageControlView(currentPage: $currentPage, numberOfPages: surveys.count)
.frame(width: .zero, alignment: .leading)
PageControlView(
currentPage: $currentPage,
numberOfPages: dataSource.surveys.count
)
.frame(width: .zero, alignment: .leading)
Spacer()
}
Spacer()
Expand Down
74 changes: 39 additions & 35 deletions iosApp/Survey/Sources/Presentation/Views/FadePaginationView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -28,47 +28,51 @@ struct FadePaginationView<T>: View {

var body: some View {
ZStack {
nextView(items[nextPage])
currentView(items[currentPage])
.opacity(currentVisibility)
.gesture(
DragGesture(minimumDistance: minimumTurnDistance, coordinateSpace: .local)
.onChanged { value in
switch value.translation.width {
case ...0: nextPage = min(items.count - 1, currentPage + 1)
case 0...: nextPage = max(0, currentPage - 1)
default: return
if items.count > nextPage {
nextView(items[nextPage])
}
if items.count > currentPage {
currentView(items[currentPage])
.opacity(currentVisibility)
.gesture(
DragGesture(minimumDistance: minimumTurnDistance, coordinateSpace: .local)
.onChanged { value in
switch value.translation.width {
case ...0: nextPage = min(items.count - 1, currentPage + 1)
case 0...: nextPage = max(0, currentPage - 1)
default: return
}
let turningVisibilityPercentage =
turningLength * turningVisibilityMultiplier / abs(value.translation.width)
currentVisibility = 1.0 * max(0.0, turningVisibilityPercentage)
}
let turningVisibilityPercentage =
turningLength * turningVisibilityMultiplier / abs(value.translation.width)
currentVisibility = 1.0 * max(0.0, turningVisibilityPercentage)
}
.onEnded { value in
withAnimation(.easeInOut(duration: .default)) {
let velocity = self.velocity
if velocity.dx < -turningSpeed {
// Swipe back fast
previousPage()
} else if velocity.dx > turningSpeed {
// Swipe forward fast
forwardPage()
} else {
// Slow swipe
switch value.translation.width {
case ...(-turningLength):
// Swipe back reaching threshold
.onEnded { value in
withAnimation(.easeInOut(duration: .default)) {
let velocity = self.velocity
if velocity.dx < -turningSpeed {
// Swipe back fast
previousPage()
case turningLength...:
// Swipe forward reaching threshold
} else if velocity.dx > turningSpeed {
// Swipe forward fast
forwardPage()
default: break
} else {
// Slow swipe
switch value.translation.width {
case ...(-turningLength):
// Swipe back reaching threshold
previousPage()
case turningLength...:
// Swipe forward reaching threshold
forwardPage()
default: break
}
}
currentVisibility = 1.0
}
currentVisibility = 1.0
}
}
.updatingVelocity($velocity)
)
.updatingVelocity($velocity)
)
} else { VStack {} }
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,3 +78,13 @@ protocol LogOutUseCaseKMM: LogOutUseCase {
@escaping (Error?, KotlinUnit) -> KotlinUnit
) -> () -> KotlinUnit
}

// sourcery: AutoMockable
protocol SurveyListUseCaseKMM: SurveyListUseCase {

func invoke(page: Int32) -> Kotlinx_coroutines_coreFlow
minhnimble marked this conversation as resolved.
Show resolved Hide resolved
func invokeNative(page: Int32) -> (
@escaping ([Survey], KotlinUnit) -> KotlinUnit,
@escaping (Error?, KotlinUnit) -> KotlinUnit
) -> () -> KotlinUnit
}
7 changes: 7 additions & 0 deletions iosApp/SurveyKIFUITests/Sources/Screens/LoginScreen.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,14 @@
final class LoginScreen: GenericScreen {

func loginIfNeeded() {
if tester.tryFindingView(withAccessibilityIdentifier: ViewId.surveySelection(.view)()) {
return
}
if tester.tryFindingView(withAccessibilityIdentifier: ViewId.splash(.view)()) {
tester.waitForAbsenceOfView(withAccessibilityIdentifier: ViewId.splash(.view)())
}
if tester.tryFindingView(withAccessibilityIdentifier: ViewId.login(.view)()) {
tester.waitForTappableView(withAccessibilityIdentifier: ViewId.login(.loginButton)())
tester.tapView(withAccessibilityIdentifier: ViewId.login(.loginButton)())
}
}
Expand Down
12 changes: 12 additions & 0 deletions iosApp/SurveyKIFUITests/Sources/Screens/SurveyScreen.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,19 @@

final class SurveyScreen: GenericScreen {

func waitForAppearance() {
tester.waitForView(withAccessibilityIdentifier: ViewId.surveySelection(.view)())
blyscuit marked this conversation as resolved.
Show resolved Hide resolved
}

func navigateToAccount() {
waitForAppearance()
tester.tapView(withAccessibilityIdentifier: ViewId.surveySelection(.headerProfileImage)())
}

func swipeRight() {
tester.swipeView(
withAccessibilityIdentifier: ViewId.surveySelection(.detailText)(),
in: .left
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
//
// SurveySelectionSpec.swift
// SurveyKIFUITests
//
// Created by Bliss on 28/12/22.
// Copyright © 2022 Nimble. All rights reserved.
//

import Nimble
import Quick

final class SurveySelectionSpec: QuickSpec {

override func spec() {

var loginScreen: LoginScreen!
var surveyScreen: SurveyScreen!

describe("an Account screen") {

beforeEach {
loginScreen = LoginScreen(self)
surveyScreen = SurveyScreen(self)
}

describe("its open") {

beforeEach {
loginScreen.loginIfNeeded()
surveyScreen.waitForAppearance()
}

describe("its image area") {

context("when swipe left multitple times") {

beforeEach {
for _ in 0 ..< 5 {
surveyScreen.swipeRight()
}
}

it("show new surveys") {
self.tester().waitForView(
withAccessibilityIdentifier: ViewId.surveySelection(.mainImage)()
)
}
}
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ final class SurveySelectionViewDataSourceSpec: QuickSpec {
var getCurrentDateUseCase: GetCurrentDateUseCaseKMMMock!
var getProfileUseCase: GetProfileUseCaseKMMMock!
var getAppVersionUseCase: GetAppVersionUseCaseKMMMock!
var surveyListUseCase: SurveyListUseCaseKMMMock!
var surveySelectionViewModel: SurveySelectionViewModel!
var dataSource: SurveySelectionView.DataSource!

Expand All @@ -28,10 +29,12 @@ final class SurveySelectionViewDataSourceSpec: QuickSpec {
getCurrentDateUseCase = GetCurrentDateUseCaseKMMMock()
getProfileUseCase = GetProfileUseCaseKMMMock()
getAppVersionUseCase = GetAppVersionUseCaseKMMMock()
surveyListUseCase = SurveyListUseCaseKMMMock()
surveySelectionViewModel = SurveySelectionViewModel(
getCurrentDateUseCase: getCurrentDateUseCase,
getProfileUseCase: getProfileUseCase,
getAppVersionUseCase: getAppVersionUseCase,
surveyListUseCase: surveyListUseCase,
dateTimeFormatter: DateTimeFormatterImpl()
)
dataSource = .init(
Expand All @@ -56,11 +59,14 @@ final class SurveySelectionViewDataSourceSpec: QuickSpec {

let user = User(name: "name", avatarUrl: "avatarUrl")
let appVersion = AppVersion(appVersion: "", buildNumber: "")
let survey = Survey(id: "", imageUrl: "", title: "", description: "")
let surveys = Array(repeating: survey, count: 3)

beforeEach {
getCurrentDateUseCase.invokeReturnValue = AnyFlow(result: KotlinLong(1))
getProfileUseCase.invokeReturnValue = AnyFlow(result: user)
getAppVersionUseCase.invokeReturnValue = AnyFlow(result: appVersion)
surveyListUseCase.invokePageReturnValue = AnyFlow(result: NSArray(array: surveys))
delayFetch()
}

Expand All @@ -73,13 +79,37 @@ final class SurveySelectionViewDataSourceSpec: QuickSpec {
let viewState = try self.awaitPublisher(dataSource.$viewState.collectNext(2)).last
expect(viewState?.surveyHeaderUiModel?.dateText) == "Thursday, January 1"
}

it("sets survey with correct item") {
let surveys = try self.awaitPublisher(dataSource.$surveys.collectNext(2)).last
expect(surveys?.count) == 3
}

describe("its checkFetchMore") {

beforeEach {
_ = try? self.awaitPublisher(dataSource.$surveys.collectNext(2))
delayCheckFetchMore()
}

it("sets survey with correct item") {
let surveys = try self.awaitPublisher(dataSource.$surveys.collectNext(1)).last
expect(surveys?.count) == 6
}
}
}
}

func delayFetch() {
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
DispatchQueue.main.asyncAfter(deadline: .now() + 0.01) {
dataSource.fetch()
}
}

func delayCheckFetchMore() {
DispatchQueue.main.asyncAfter(deadline: .now() + 0.01) {
dataSource.checkFetchMore(index: 3)
}
}
}
}
Loading