I'd line to UI test a Custom Layout in a library module that doesn't have any Activities or Fragments. Is that even possible? All the information I can find about UI testing with Espresso needs an ActivityScenario or Fragment scenario, which I don't have in this case.
Here's the code of my Custom layout class, if that helps
class BuffView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0)
: LinearLayout(context, attrs, defStyleAttr) {
private val buffView: LinearLayout = inflate(context, R.layout.buff_view, this) as LinearLayout
private var errorListener: ErrorListener? = null
private val apiErrorHandler = ApiErrorHandler()
private val getBuffUseCase = GetBuffUseCase(apiErrorHandler)
/**
* Handler for the time intervals between requests
*/
private val intervalsHandler = Handler()
private var countDownTimer: CountDownTimer? = null
private var buffIdCount = 1
/**
* Indicates whether the API request should be made or not
*/
private var getBuffs = false
fun init() {
getBuffs = true
getBuff()
}
private fun getBuff() {
if (!getBuffs) return
getBuffUseCase.invoke(Params(buffIdCount.toLong()), object : UseCaseResponse<Buff> {
override fun onSuccess(result: Buff) {
if (isDataValid(result)) displayBuff(result) else hideBuffView()
}
override fun onError(errorModel: ErrorModel?) {
errorListener?.onError(
errorModel?.message ?: context.getString(R.string.generic_error_message)
)
hideBuffView()
}
})
if (buffIdCount < NUM_BUFFS_TO_BE_FETCHED ) {
intervalsHandler.postDelayed({
buffIdCount++
getBuff()
stopCountDownTimer()
}, REQUEST_BUFF_INTERVAL_TIME)
}
}
private fun isDataValid(buff: Buff): Boolean {
if (buff.author.firstName.isNullOrEmpty() && buff.author.lastName.isNullOrEmpty()) {
showErrorInvalidData(context.getString(R.string.author_reason_error_message))
return false
}
if (buff.question == null || buff.question.title.isNullOrEmpty()) {
showErrorInvalidData(context.getString(R.string.question_reason_error_message))
return false
}
if (buff.timeToShow == null || buff.timeToShow < 0) {
showErrorInvalidData(context.getString(R.string.timer_reason_error_message))
return false
}
if (buff.answers == null || buff.answers.size < 2) {
showErrorInvalidData(context.getString(R.string.answers_reason_error_message))
return false
}
return true
}
private fun showErrorInvalidData(reason: String) {
errorListener?.onError(context.getString(R.string.data_incomplete_error_message, reason))
}
private fun displayBuff(buff: Buff) {
setBuffQuestion(buff.question!!.title!!)
setBuffAuthor(buff.author)
setBuffAnswers(buff.answers!!)
setBuffProgressBar(buff.timeToShow!!)
setBuffCloseButton()
invalidate()
showBuffView()
}
private fun setBuffQuestion(question: String) {
questionText.text = question
}
private fun setBuffAuthor(author: Buff.Author) {
val firstName = author.firstName ?: ""
val lastName = author.lastName ?: ""
val fullName = "$firstName $lastName"
authorName.text = fullName
author.image?.let { authorImageUrl ->
Glide.with(context)
.load(authorImageUrl)
.into(authorImage)
}
}
/**
* Adds a new answerView for each element in answers
* */
private fun setBuffAnswers(answers: List<Buff.Answer>) {
val answersContainer = findViewById<LinearLayout>(R.id.answersContainer)
answersContainer.removeAllViews()
for(answer in answers) {
val answerView: View = LayoutInflater.from(answersContainer.context).inflate(
R.layout.buff_answer,
answersContainer,
false
)
answer.answerImage?.x0?.url?.let {
Glide.with(context)
.load(it)
.into(answerView.answerImage)
}
answerView.setOnClickListener {
answerView.background = ContextCompat.getDrawable(
context,
R.drawable.answer_selected_bg
)
answerView.answerText.setTextColor(
ContextCompat.getColor(
context,
android.R.color.white
)
)
//freeze timer on answer selected
stopCountDownTimer()
//hide buff_content view 2 seconds after an answer has been selected
it.postDelayed({
hideBuffView()
}, HIDE_BUFF_AFTER_SELECTED_ANSWER_DURATION)
}
answerView.answerText?.text = answer.title
answersContainer.addView(answerView)
}
}
private fun setBuffProgressBar(timeToShow: Int) {
questionTimeProgress.max = timeToShow
countDownTimer = object : CountDownTimer(
timeToShow * ONE_SECOND_INTERVAL,
ONE_SECOND_INTERVAL
) {
override fun onTick(millisUntilFinished: Long) {
questionTimeText.text = (millisUntilFinished / ONE_SECOND_INTERVAL).toString()
questionTimeProgress.progress = timeToShow - (millisUntilFinished / ONE_SECOND_INTERVAL).toInt()
}
override fun onFinish() {
hideBuffView()
}
}.start()
}
private fun showBuffView() {
buffView.visibility = VISIBLE
val entryAnimation = AnimationUtils.loadAnimation(context, R.anim.entry_anim)
buffView.startAnimation(entryAnimation)
}
private fun hideBuffView() {
buffView.visibility = GONE
val exitAnimation = AnimationUtils.loadAnimation(context, R.anim.exit_anim)
buffView.startAnimation(exitAnimation)
}
private fun setBuffCloseButton() {
buffCloseImageButton.setOnClickListener {
hideBuffView()
stopCountDownTimer()
}
}
private fun stopCountDownTimer() {
countDownTimer?.cancel()
}
fun addErrorListener(errorListener: ErrorListener) {
this.errorListener = errorListener
}
}
Aucun commentaire:
Enregistrer un commentaire