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

[스캇] 1, 2단계 영화 극장 선택 제출합니다. #23

Merged
merged 32 commits into from
Apr 30, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
0991106
feat: merge
RightHennessy Apr 25, 2023
79905b9
docs: 기능 목록 추가
RightHennessy Apr 25, 2023
09116ef
feat: 바텀네비게이션 xml 구현
RightHennessy Apr 25, 2023
5d01561
feat: 메인 엑티비티 초기 구현
RightHennessy Apr 25, 2023
af1fc7f
feat: setting fragment 기능 구현
RightHennessy Apr 25, 2023
a42ecc7
feat: MoviesFragment 구현
chws0508 Apr 25, 2023
9c2cc90
feat: reservation fragment xml 구현
chws0508 Apr 25, 2023
1de0306
feat: reservationList fragment 구현
chws0508 Apr 25, 2023
3434e71
feat: 바텀 네비게이션 동작 구현
chws0508 Apr 26, 2023
a0b5c81
feat: 예매 내역을 터치하면 예매 정보를 보여준다.
chws0508 Apr 26, 2023
9b23515
feat: 클릭 시 효과주기 구현
chws0508 Apr 26, 2023
48f3d2d
docs: step2 기능 목록 구현
chws0508 Apr 26, 2023
9983bf9
docs: 설정 화면 구현
chws0508 Apr 26, 2023
7189bf3
feat : 권한 요청 구현
chws0508 Apr 26, 2023
2416121
feat : 푸시 알림 정보 저장
chws0508 Apr 26, 2023
3700c4b
feat : 알림 기능 구현
chws0508 Apr 26, 2023
bd4343d
feat : 푸시 알림 on/off 적용
chws0508 Apr 27, 2023
a5d6aef
refactor: 함수 분리
RightHennessy Apr 27, 2023
114d4e2
feat : 설정 화면 UI Test 구현
chws0508 Apr 27, 2023
d0c5c66
feat : NotificationHelper 로 분리
chws0508 Apr 27, 2023
93480e9
feat : Test 코드 패키지 일치
chws0508 Apr 28, 2023
a65f934
feat : 테스트명 한글로 수정
chws0508 Apr 28, 2023
eede551
feat : SettingPreferenceManager 오브젝트 구현
chws0508 Apr 30, 2023
a5705cb
feat : PushAlarmSwitch 클래스 구현
chws0508 Apr 30, 2023
70e6f0b
refactor : Fragment 는 switch 가 켜져있는지만 알 수 있도록 수정
chws0508 Apr 30, 2023
04cf373
refactor : Fragment Test 수정
chws0508 Apr 30, 2023
6bd969b
refactor : onViewCreated 에서 View 작업을 하도록 수정
chws0508 Apr 30, 2023
66bc737
refactor : Permission 요청 함수 분리 및, SettingPreferenceManager 오브젝트 패키지 이동
chws0508 Apr 30, 2023
d962bd5
refactor : 화면 회전시 기존 fragmet 유지
chws0508 Apr 30, 2023
b0e1f66
refactor : onCreateView 삭제
chws0508 Apr 30, 2023
267e92c
refactor : ReservationAlarmManager 구현
chws0508 Apr 30, 2023
e148872
refactor : NotificationReceiver 수정
chws0508 Apr 30, 2023
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
17 changes: 13 additions & 4 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ android {
isMinifyEnabled = false
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
"proguard-rules.pro",
)
}
}
Expand All @@ -33,14 +33,23 @@ android {
kotlinOptions {
jvmTarget = "11"
}
testOptions {
animationsDisabled = true
}
}

dependencies {
implementation("androidx.core:core-ktx:1.9.0")
implementation("androidx.appcompat:appcompat:1.6.0")
implementation("com.google.android.material:material:1.7.0")
implementation(project(":domain"))
implementation("androidx.core:core-ktx:1.10.0")
implementation("androidx.appcompat:appcompat:1.6.1")
implementation("com.google.android.material:material:1.8.0")
implementation("androidx.constraintlayout:constraintlayout:2.1.4")
implementation("androidx.fragment:fragment-ktx:1.5.7")
testImplementation("junit:junit:4.13.2")
androidTestImplementation("androidx.test.ext:junit:1.1.5")
androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1")
androidTestImplementation("androidx.test.espresso:espresso-intents:3.4.0")
debugImplementation("androidx.fragment:fragment-ktx:1.6.0-beta01")
debugImplementation("androidx.fragment:fragment-testing:1.5.7")
implementation("androidx.preference:preference:1.2.0")
}
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,215 @@
package woowacourse.movie.activity

import android.content.Intent
import androidx.test.core.app.ApplicationProvider
import androidx.test.espresso.Espresso.onView
import androidx.test.espresso.action.ViewActions.click
import androidx.test.espresso.assertion.ViewAssertions.matches
import androidx.test.espresso.intent.Intents
import androidx.test.espresso.intent.matcher.IntentMatchers
import androidx.test.espresso.matcher.ViewMatchers.*
import androidx.test.ext.junit.rules.ActivityScenarioRule
import androidx.test.ext.junit.runners.AndroidJUnit4
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import woowacourse.movie.R
import woowacourse.movie.view.model.*
import java.time.LocalDate
import java.time.LocalDateTime
import java.time.LocalTime

@RunWith(AndroidJUnit4::class)
class SelectSeatActivityTest {
private val movieUiModel = MovieUiModel(0, "해리포터", LocalDate.MAX, LocalDate.MIN, 0, "")
private val date = LocalDate.of(2023, 4, 22)
private val time = LocalTime.of(9, 0)
private val dateTime: LocalDateTime = LocalDateTime.of(date, time)
private val ticketDateUiModel = TicketDateUiModel(dateTime)

private val intent =
Intent(ApplicationProvider.getApplicationContext(), SelectSeatActivity::class.java).apply {
putExtra("movie", movieUiModel)
putExtra("peopleCount", 3)
putExtra("ticket", ticketDateUiModel)
}

@get:Rule
val activityRule = ActivityScenarioRule<SelectSeatActivity>(intent)

@Test
fun 좌석이_생성되는지_확인한다() {
// given
val seatFirst = onView(withText("A1"))
val seatLast = onView(withText("E4"))
// then
seatFirst.check(matches(isDisplayed()))
seatLast.check(matches(isDisplayed()))
}

@Test
fun 선택되지_않았던_좌석을_클릭했을때_선택된_상태로_바뀐다(){
// given
val seat = onView(withText("A1"))
// when
seat.perform(click())
// then
seat.check(matches(isSelected()))
}

@Test
fun 선택했던_좌석을_클릭했을때_선택하지_않은_상태로_바뀐다(){
// given
val seat = onView(withText("A1"))
// when
seat.perform(click())
seat.perform(click())
// then
seat.check(matches(isNotSelected()))
}

@Test
fun 인원수와_같은_수만큼_좌석을_클릭하면_더이상_좌석이_선택된_상태로_변경되지_않는다(){
// given
val seat1 = onView(withText("A1"))
val seat2 = onView(withText("A2"))
val seat3 = onView(withText("A3"))
val seat4 = onView(withText("B4"))
// when
seat1.perform(click())
seat2.perform(click())
seat3.perform(click())
seat4.perform(click())
// then
seat4.check(matches(isNotSelected()))
}

@Test
fun 조조할인을_받고_B등급_좌석을_클릭하면_가격텍스트가_8000원_으로_바뀐다(){
// given
val seat = onView(withText("A1"))
val price = onView(withId(R.id.select_seat_price_text_view))
// when
seat.perform(click())
// then
price.check(matches(withText("8,000원")))
}

@Test
fun 조조할인을_받고_A등급_좌석을_클릭하면_가격텍스트가_10000원_으로_바뀐다(){
// given
val seat = onView(withText("E1"))
val price = onView(withId(R.id.select_seat_price_text_view))
// when
seat.perform(click())
// then
price.check(matches(withText("10,000원")))
}

@Test
fun 조조할인을_받고_S등급_좌석을_클릭하면_가격텍스트가_13000원_으로_바뀐다(){
// given
val seat = onView(withText("C1"))
val price = onView(withId(R.id.select_seat_price_text_view))
// when
seat.perform(click())
// then
price.check(matches(withText("13,000원")))
}

@Test
fun 조조할인을_받고_S등급_좌석과_A등급_좌석을_클릭하면_가격텍스트가_23000원_으로_바뀐다(){
// given
val seatS = onView(withText("C1"))
val seatA = onView(withText("E1"))
val price = onView(withId(R.id.select_seat_price_text_view))
// when
seatS.perform(click())
seatA.perform(click())
// then
price.check(matches(withText("23,000원")))
}

@Test
fun 인원수와_같은_수만큼_좌석이_선택된_상태가_아니라면_버튼을_클릭할_수_없다(){
// given
val seat1 = onView(withText("A1"))
val seat2 = onView(withText("A2"))
val button = onView(withText("확인"))
// when
seat1.perform(click())
seat2.perform(click())
// then
button.check(matches(isNotClickable()))
}

@Test
fun 인원수와_같은_수만큼_좌석이_선택된_상태이면_버튼을_클릭할_수_있다(){
// given
val seat1 = onView(withText("A1"))
val seat2 = onView(withText("A2"))
val seat3 = onView(withText("A3"))
val button = onView(withText("확인"))
// when
seat1.perform(click())
seat2.perform(click())
seat3.perform(click())
// then
button.check(matches(isClickable()))
}

@Test
fun 인원수와_같은_수만큼_좌석이_선택된_상태에서_버튼을_클릭하면_다이얼로그가_나온다(){
// given
val seat1 = onView(withText("A1"))
val seat2 = onView(withText("A2"))
val seat3 = onView(withText("A3"))
val button = onView(withText("확인"))
// when
seat1.perform(click())
seat2.perform(click())
seat3.perform(click())
Thread.sleep(2000)
button.perform(click())
// then
onView(withText(R.string.select_seat_dialog_title)).check(matches(isDisplayed()))
}

@Test
fun 다이얼로그가_나온_상태에서_배경을_클릭해도_다이얼로그가_사라지지_않는다(){
// given
val seat1 = onView(withText("A1"))
val seat2 = onView(withText("A2"))
val seat3 = onView(withText("A3"))
val button = onView(withText("확인"))
// when
seat1.perform(click())
seat2.perform(click())
seat3.perform(click())
button.perform(click())
val dialog = onView(withText(R.string.select_seat_dialog_title))
onView(isRoot()).perform(click())
// then
(dialog).check(matches(isDisplayed()))
}

@Test
fun 다이얼로그가_나온_상태에서_예매확인을_클릭하면_예매결과창으로_넘어간다(){
// given
Intents.init()
val seat1 = onView(withText("A1"))
val seat2 = onView(withText("A2"))
val seat3 = onView(withText("A3"))
val button = onView(withText("확인"))
val dialogPositiveButton = onView(withText("예매 완료"))
// when
seat1.perform(click())
seat2.perform(click())
seat3.perform(click())
button.perform(click())
dialogPositiveButton.perform(click())
// then
Intents.intended(IntentMatchers.hasComponent(ReservationResultActivity::class.java.name))
Intents.release()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
package woowacourse.movie.fragment

import android.widget.Switch
import androidx.fragment.app.testing.FragmentScenario
import androidx.fragment.app.testing.launchFragmentInContainer
import androidx.test.espresso.Espresso.onView
import androidx.test.espresso.action.ViewActions.click
import androidx.test.espresso.assertion.ViewAssertions.matches
import androidx.test.espresso.matcher.ViewMatchers.*
import androidx.test.ext.junit.runners.AndroidJUnit4
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import woowacourse.movie.R

@RunWith(AndroidJUnit4::class)
class SettingFragmentTest {
private var fragmentScenario: FragmentScenario<SettingFragment>? = null
private var isAlarmOn: Boolean = false

@Before
fun 프레그먼트_띄우기() {
fragmentScenario = launchFragmentInContainer()
fragmentScenario?.onFragment {
isAlarmOn =
it.requireActivity().findViewById<Switch>(R.id.setting_push_alarm_switch).isChecked
Comment on lines +24 to +26

Choose a reason for hiding this comment

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

실제 UI를 기반으로 테스트 환경이 맞춰졌네요. 👍🏻

}
}

@Test
fun 스위치가_화면에_뜨는지_확인한다() {
val switch = onView(withId(R.id.setting_push_alarm_switch))
switch.check(matches(isDisplayed()))
}

@Test
fun `꺼져있는_스위치를_클릭하면_켜진다`() {
// given
val switch = onView(withId(R.id.setting_push_alarm_switch))
if (isAlarmOn) switch.perform(click())
// when
switch.perform(click())
// then
switch.check(matches(isChecked()))
}

@Test
fun `켜져있는_스위치를_클릭하면_꺼진다`() {
// given
val switch = onView(withId(R.id.setting_push_alarm_switch))
if (!isAlarmOn) switch.perform(click())
// when
switch.perform(click())
// then
switch.check(matches(isNotChecked()))
}

@Test
fun `스위치를_켜져있는_상태로_앱을_재실행하면_스위치가_켜져있는_상태로_나온다`() {
// given
val switch = onView(withId(R.id.setting_push_alarm_switch))
if (!isAlarmOn) switch.perform(click())
// when
fragmentScenario?.recreate()
// then
switch.check(matches(isChecked()))
}

@Test
fun `스위치를_꺼져있는_상태로_앱을_재실행하면_스위치가_꺼져있는_상태로_나온다`() {
// given
val switch = onView(withId(R.id.setting_push_alarm_switch))
if (isAlarmOn) switch.perform(click())
// when
fragmentScenario?.recreate()
// then
switch.check(matches(isNotChecked()))
}
}
42 changes: 31 additions & 11 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -1,25 +1,45 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
xmlns:tools="http://schemas.android.com/tools">

<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />

<application
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/Theme.Movie"
tools:targetApi="31">
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/Theme.Movie"
tools:targetApi="31">
<activity
android:name=".MainActivity"
android:exported="true">
android:name=".activity.MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".activity.SelectSeatActivity"
android:exported="false"></activity>
<activity
android:name=".activity.ReservationResultActivity"
android:exported="false" />
<activity
android:name=".activity.MovieReservationActivity"
android:exported="true" />

<receiver
android:name=".receiver.ReservationNotificationReceiver"
android:enabled="true"
android:exported="false">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
</application>

</manifest>
Loading