Correcting API errors, starting the displaying of bets and adding simple UI tests
continuous-integration/drone/push Build is passing
Details
continuous-integration/drone/push Build is passing
Details
parent
6f559862e4
commit
038781de5a
@ -1,24 +0,0 @@
|
||||
package fr.iut.alldev.allin
|
||||
|
||||
import androidx.test.platform.app.InstrumentationRegistry
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
|
||||
import org.junit.Assert.*
|
||||
|
||||
/**
|
||||
* Instrumented test, which will execute on an Android device.
|
||||
*
|
||||
* See [testing documentation](http://d.android.com/tools/testing).
|
||||
*/
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class ExampleInstrumentedTest {
|
||||
@Test
|
||||
fun useAppContext() {
|
||||
// Context of the app under test.
|
||||
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
|
||||
assertEquals("fr.iut.alldev.allin", appContext.packageName)
|
||||
}
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
package fr.iut.alldev.allin
|
||||
|
||||
import android.app.Application
|
||||
import android.content.Context
|
||||
import androidx.test.runner.AndroidJUnitRunner
|
||||
import dagger.hilt.android.testing.HiltTestApplication
|
||||
|
||||
class TestRunner : AndroidJUnitRunner() {
|
||||
|
||||
override fun newApplication(cl: ClassLoader?, name: String?, context: Context?): Application {
|
||||
return super.newApplication(cl, HiltTestApplication::class.java.name, context)
|
||||
}
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
package fr.iut.alldev.allin.test.finders
|
||||
|
||||
import androidx.compose.ui.test.*
|
||||
|
||||
fun SemanticsNodeInteraction.onChildWith(matcher: SemanticsMatcher) = onChildren().filterToOne(matcher)
|
||||
|
||||
fun SemanticsNodeInteraction.onChildWithTag(testTag: String) = onChildWith(hasTestTag(testTag))
|
@ -0,0 +1,31 @@
|
||||
package fr.iut.alldev.allin.test.mock
|
||||
|
||||
import fr.iut.alldev.allin.data.model.bet.BetStatus
|
||||
import fr.iut.alldev.allin.data.model.bet.MatchBet
|
||||
import fr.iut.alldev.allin.data.model.bet.YesNoBet
|
||||
import java.time.ZonedDateTime
|
||||
|
||||
object Bets {
|
||||
val bets by lazy{
|
||||
listOf(
|
||||
YesNoBet(
|
||||
theme = "Theme",
|
||||
phrase = "Phrase",
|
||||
endRegisterDate = ZonedDateTime.now(),
|
||||
endBetDate = ZonedDateTime.now(),
|
||||
isPublic = true,
|
||||
betStatus = BetStatus.IN_PROGRESS
|
||||
),
|
||||
MatchBet(
|
||||
theme = "Theme",
|
||||
phrase = "Phrase",
|
||||
endRegisterDate = ZonedDateTime.now(),
|
||||
endBetDate = ZonedDateTime.now(),
|
||||
isPublic = true,
|
||||
betStatus = BetStatus.IN_PROGRESS,
|
||||
nameTeam1 = "Team_1",
|
||||
nameTeam2 = "Team_2"
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
@ -0,0 +1,66 @@
|
||||
package fr.iut.alldev.allin.vo.bet
|
||||
|
||||
import androidx.activity.compose.setContent
|
||||
import androidx.compose.ui.test.junit4.createAndroidComposeRule
|
||||
import androidx.compose.ui.test.onNodeWithTag
|
||||
import dagger.hilt.android.testing.HiltAndroidRule
|
||||
import dagger.hilt.android.testing.HiltAndroidTest
|
||||
import fr.iut.alldev.allin.test.TestTags
|
||||
import fr.iut.alldev.allin.test.mock.Bets
|
||||
import fr.iut.alldev.allin.ui.MainActivity
|
||||
import fr.iut.alldev.allin.ui.theme.AllInTheme
|
||||
import fr.iut.alldev.allin.vo.bet.factory.toBetVO
|
||||
import fr.iut.alldev.allin.vo.bet.visitor.BetTestVisitor
|
||||
import org.junit.Before
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
|
||||
@HiltAndroidTest
|
||||
class BetVOTest {
|
||||
|
||||
@get:Rule
|
||||
var hiltRule = HiltAndroidRule(this)
|
||||
|
||||
@get:Rule
|
||||
val composeTestRule = createAndroidComposeRule<MainActivity>()
|
||||
|
||||
@Before
|
||||
fun init() {
|
||||
hiltRule.inject()
|
||||
}
|
||||
|
||||
companion object {
|
||||
val visitor = BetTestVisitor()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testVisitor_shouldDisplayYesNoBetUI(){
|
||||
//Given
|
||||
|
||||
//When
|
||||
composeTestRule.activity.setContent {
|
||||
AllInTheme{
|
||||
Bets.bets[0].toBetVO()?.accept(v = visitor)
|
||||
}
|
||||
}
|
||||
//Expect
|
||||
composeTestRule.onNodeWithTag(TestTags.YES_NO_BET.tag).assertExists()
|
||||
composeTestRule.onNodeWithTag(TestTags.MATCH_BET.tag).assertDoesNotExist()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testVisitor_shouldDisplayMatchUI(){
|
||||
//Given
|
||||
|
||||
//When
|
||||
composeTestRule.activity.setContent {
|
||||
AllInTheme{
|
||||
Bets.bets[1].toBetVO()?.accept(v = visitor)
|
||||
}
|
||||
}
|
||||
//Expect
|
||||
composeTestRule.onNodeWithTag(TestTags.MATCH_BET.tag).assertExists()
|
||||
composeTestRule.onNodeWithTag(TestTags.YES_NO_BET.tag).assertDoesNotExist()
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
package fr.iut.alldev.allin.vo.bet.visitor
|
||||
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.testTag
|
||||
import fr.iut.alldev.allin.data.model.bet.MatchBet
|
||||
import fr.iut.alldev.allin.data.model.bet.YesNoBet
|
||||
import fr.iut.alldev.allin.test.TestTags
|
||||
|
||||
class BetTestVisitor : DisplayBetVisitor {
|
||||
@Composable
|
||||
override fun visitYesNoBet(b: YesNoBet) {
|
||||
Text("This is a YesNo Bet", Modifier.testTag(TestTags.YES_NO_BET.tag))
|
||||
}
|
||||
|
||||
@Composable
|
||||
override fun visitMatchBet(b: MatchBet) {
|
||||
Text("This is a Match Bet", Modifier.testTag(TestTags.MATCH_BET.tag))
|
||||
}
|
||||
}
|
@ -0,0 +1,6 @@
|
||||
package fr.iut.alldev.allin.test
|
||||
|
||||
enum class TestTags(val tag: String) {
|
||||
YES_NO_BET("YES_NO"),
|
||||
MATCH_BET("MATCH_BET")
|
||||
}
|
@ -0,0 +1,71 @@
|
||||
package fr.iut.alldev.allin.ui.betstatus.components
|
||||
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.font.FontStyle
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import fr.iut.alldev.allin.R
|
||||
import fr.iut.alldev.allin.data.ext.toPercentageString
|
||||
import fr.iut.alldev.allin.ui.core.PercentagePositionnedElement
|
||||
import fr.iut.alldev.allin.ui.core.StatBar
|
||||
import fr.iut.alldev.allin.ui.theme.AllInTheme
|
||||
|
||||
@Composable
|
||||
fun YesNoStatBar(
|
||||
yesPercentage: Float
|
||||
) {
|
||||
Column(
|
||||
Modifier.padding(horizontal = 9.dp)
|
||||
){
|
||||
Row{
|
||||
Text(
|
||||
text = stringResource(id = R.string.Yes).uppercase(),
|
||||
color = AllInTheme.colors.allIn_Blue,
|
||||
style = AllInTheme.typography.h1,
|
||||
fontStyle = FontStyle.Italic,
|
||||
fontSize = 30.sp,
|
||||
modifier = Modifier.weight(1f)
|
||||
)
|
||||
Text(
|
||||
text = stringResource(id = R.string.No).uppercase(),
|
||||
style = AllInTheme.typography.h1,
|
||||
fontStyle = FontStyle.Italic,
|
||||
fontSize = 30.sp,
|
||||
color = AllInTheme.colors.allIn_BarPink
|
||||
)
|
||||
}
|
||||
StatBar(percentage = yesPercentage)
|
||||
PercentagePositionnedElement(
|
||||
percentage = yesPercentage
|
||||
){
|
||||
Text(
|
||||
text = yesPercentage.toPercentageString(),
|
||||
style = AllInTheme.typography.h3,
|
||||
color = AllInTheme.colors.allIn_BarPurple
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private class YesNoStatBarPreviewProvider: PreviewParameterProvider<Float> {
|
||||
override val values = sequenceOf(0f, .33f, .5f, .66f, 1f)
|
||||
}
|
||||
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
private fun YesNoStatBarPreview(
|
||||
@PreviewParameter(YesNoStatBarPreviewProvider::class) percentage: Float
|
||||
) {
|
||||
AllInTheme {
|
||||
YesNoStatBar(percentage)
|
||||
}
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
package fr.iut.alldev.allin.ui.betstatus.visitor
|
||||
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.dp
|
||||
import fr.iut.alldev.allin.data.model.bet.MatchBet
|
||||
import fr.iut.alldev.allin.data.model.bet.YesNoBet
|
||||
import fr.iut.alldev.allin.ui.core.StatBar
|
||||
import fr.iut.alldev.allin.ui.core.bet.BetTitleHeader
|
||||
import fr.iut.alldev.allin.vo.bet.visitor.DisplayBetVisitor
|
||||
|
||||
class BetStatusBottomSheetDisplayBetVisitor : DisplayBetVisitor {
|
||||
@Composable
|
||||
override fun visitYesNoBet(b: YesNoBet) {
|
||||
Column {
|
||||
BetTitleHeader(
|
||||
title = b.phrase,
|
||||
category = b.theme,
|
||||
creator = "Lucas" /*TODO : Creator*/,
|
||||
modifier = Modifier.padding(horizontal = 20.dp)
|
||||
)
|
||||
StatBar(percentage = .86f)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
override fun visitMatchBet(b: MatchBet) {
|
||||
Text("This is a MATCH BET")
|
||||
}
|
||||
}
|
@ -0,0 +1,53 @@
|
||||
package fr.iut.alldev.allin.ui.core
|
||||
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.dp
|
||||
import fr.iut.alldev.allin.ui.theme.AllInTheme
|
||||
|
||||
@Composable
|
||||
fun PercentagePositionnedElement(
|
||||
percentage: Float,
|
||||
offset: Dp = (-9).dp,
|
||||
content: @Composable ()->Unit
|
||||
) {
|
||||
Box(
|
||||
Modifier.fillMaxWidth()
|
||||
) {
|
||||
when (percentage) {
|
||||
0f -> {
|
||||
content()
|
||||
}
|
||||
1f -> {
|
||||
Box(
|
||||
Modifier.align(Alignment.CenterEnd)
|
||||
){
|
||||
content()
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
Row {
|
||||
Spacer(modifier = Modifier.fillMaxWidth(percentage))
|
||||
Box(Modifier.offset(x = offset)) {
|
||||
content()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
private fun PercentagePositionnedElementPreview() {
|
||||
AllInTheme {
|
||||
PercentagePositionnedElement(percentage = .4f) {
|
||||
Text("MyElement")
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
package fr.iut.alldev.allin.vo
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
|
||||
interface ViewObject<V>{
|
||||
@Composable
|
||||
fun accept(v: V)
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
package fr.iut.alldev.allin.vo.bet
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import fr.iut.alldev.allin.data.model.bet.Bet
|
||||
import fr.iut.alldev.allin.data.model.bet.MatchBet
|
||||
import fr.iut.alldev.allin.data.model.bet.YesNoBet
|
||||
import fr.iut.alldev.allin.vo.ViewObject
|
||||
import fr.iut.alldev.allin.vo.bet.visitor.DisplayBetVisitor
|
||||
|
||||
abstract class BetVO<T: Bet>(val bet: T)
|
||||
: ViewObject<DisplayBetVisitor> {
|
||||
@Composable
|
||||
abstract override fun accept(v: DisplayBetVisitor)
|
||||
}
|
||||
|
||||
class YesNoBetVO(bet: YesNoBet) : BetVO<YesNoBet>(bet){
|
||||
@Composable
|
||||
override fun accept(v: DisplayBetVisitor){
|
||||
v.visitYesNoBet(b = bet)
|
||||
}
|
||||
}
|
||||
|
||||
class MatchBetVO(bet: MatchBet) : BetVO<MatchBet>(bet){
|
||||
@Composable
|
||||
override fun accept(v: DisplayBetVisitor){
|
||||
v.visitMatchBet(b = bet)
|
||||
}
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
package fr.iut.alldev.allin.vo.bet.factory
|
||||
|
||||
import fr.iut.alldev.allin.data.model.bet.Bet
|
||||
import fr.iut.alldev.allin.data.model.bet.MatchBet
|
||||
import fr.iut.alldev.allin.data.model.bet.YesNoBet
|
||||
import fr.iut.alldev.allin.vo.bet.BetVO
|
||||
import fr.iut.alldev.allin.vo.bet.MatchBetVO
|
||||
import fr.iut.alldev.allin.vo.bet.YesNoBetVO
|
||||
|
||||
private val betTypeToVOMap = mapOf(
|
||||
YesNoBet::class.java to YesNoBetVOFactory(),
|
||||
MatchBet::class.java to MatchBetVOFactory()
|
||||
)
|
||||
|
||||
abstract class BetVOFactory<out T : Bet> {
|
||||
abstract fun create(bet: @UnsafeVariance T): BetVO<@UnsafeVariance T>
|
||||
}
|
||||
|
||||
class YesNoBetVOFactory : BetVOFactory<YesNoBet>() {
|
||||
override fun create(bet: YesNoBet) =
|
||||
YesNoBetVO(bet)
|
||||
}
|
||||
|
||||
class MatchBetVOFactory : BetVOFactory<MatchBet>() {
|
||||
override fun create(bet: MatchBet) =
|
||||
MatchBetVO(bet)
|
||||
}
|
||||
|
||||
fun Bet.toBetVO() =
|
||||
betTypeToVOMap[this.javaClass]?.create(this)
|
@ -0,0 +1,14 @@
|
||||
package fr.iut.alldev.allin.vo.bet.visitor
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import fr.iut.alldev.allin.data.model.bet.MatchBet
|
||||
import fr.iut.alldev.allin.data.model.bet.YesNoBet
|
||||
|
||||
|
||||
interface DisplayBetVisitor {
|
||||
@Composable
|
||||
fun visitYesNoBet(b: YesNoBet)
|
||||
|
||||
@Composable
|
||||
fun visitMatchBet(b: MatchBet)
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
package fr.iut.alldev.allin.data.api.interceptors
|
||||
|
||||
import okhttp3.Interceptor
|
||||
import okhttp3.Response
|
||||
import okio.IOException
|
||||
|
||||
open class AllInAPIException(message: String) : IOException(message)
|
||||
class AllInNotFoundException(message: String) : AllInAPIException(message)
|
||||
class AllInUnauthorizedException(message: String) : AllInAPIException(message)
|
||||
class AllInUnsuccessfulException(message: String) : AllInAPIException(message)
|
||||
|
||||
|
||||
class ErrorInterceptor : Interceptor {
|
||||
override fun intercept(chain: Interceptor.Chain): Response {
|
||||
val request = chain.request()
|
||||
val response = chain.proceed(request)
|
||||
if(!response.isSuccessful){
|
||||
when (response.code) {
|
||||
404 -> throw AllInNotFoundException(response.message)
|
||||
401 -> throw AllInUnauthorizedException(response.message)
|
||||
else -> throw AllInUnsuccessfulException(response.message)
|
||||
}
|
||||
}
|
||||
if (response.body?.contentType()?.subtype != "json") {
|
||||
throw AllInAPIException(response.message)
|
||||
}
|
||||
|
||||
return response
|
||||
}
|
||||
}
|
@ -0,0 +1,3 @@
|
||||
package fr.iut.alldev.allin.data.ext
|
||||
|
||||
fun Float.toPercentageString(precision: Int = 0) = String.format("%.${precision}f", this*100) + "%"
|
@ -0,0 +1,12 @@
|
||||
package fr.iut.alldev.allin.data.model.bet
|
||||
|
||||
import java.time.ZonedDateTime
|
||||
|
||||
abstract class Bet(
|
||||
val theme: String,
|
||||
val phrase: String,
|
||||
val endRegisterDate: ZonedDateTime,
|
||||
val endBetDate: ZonedDateTime,
|
||||
val isPublic: Boolean,
|
||||
val betStatus: BetStatus,
|
||||
)
|
@ -1,4 +1,4 @@
|
||||
package fr.iut.alldev.allin.data.model
|
||||
package fr.iut.alldev.allin.data.model.bet
|
||||
|
||||
enum class BetStatus {
|
||||
FINISHED,
|
@ -1,4 +1,4 @@
|
||||
package fr.iut.alldev.allin.data.model
|
||||
package fr.iut.alldev.allin.data.model.bet
|
||||
|
||||
enum class BetType {
|
||||
YES_NO,
|
@ -0,0 +1,21 @@
|
||||
package fr.iut.alldev.allin.data.model.bet
|
||||
|
||||
import java.time.ZonedDateTime
|
||||
|
||||
class MatchBet(
|
||||
theme: String,
|
||||
phrase: String,
|
||||
endRegisterDate: ZonedDateTime,
|
||||
endBetDate: ZonedDateTime,
|
||||
isPublic: Boolean,
|
||||
betStatus: BetStatus,
|
||||
val nameTeam1: String,
|
||||
val nameTeam2: String
|
||||
) : Bet(
|
||||
theme,
|
||||
phrase,
|
||||
endRegisterDate,
|
||||
endBetDate,
|
||||
isPublic,
|
||||
betStatus
|
||||
)
|
@ -0,0 +1,19 @@
|
||||
package fr.iut.alldev.allin.data.model.bet
|
||||
|
||||
import java.time.ZonedDateTime
|
||||
|
||||
class YesNoBet(
|
||||
theme: String,
|
||||
phrase: String,
|
||||
endRegisterDate: ZonedDateTime,
|
||||
endBetDate: ZonedDateTime,
|
||||
isPublic: Boolean,
|
||||
betStatus: BetStatus
|
||||
) : Bet(
|
||||
theme,
|
||||
phrase,
|
||||
endRegisterDate,
|
||||
endBetDate,
|
||||
isPublic,
|
||||
betStatus
|
||||
)
|
Loading…
Reference in new issue