Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
24 changes: 24 additions & 0 deletions .github/workflows/on-main-merge.yml
Original file line number Diff line number Diff line change
Expand Up @@ -138,3 +138,27 @@ jobs:
api-level: 36
arch: x86_64
script: ./gradlew connectedDebugAndroidTest

qodana:
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: write
checks: write

steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0 # a full history is required for analysis

- name: 'Qodana Scan'
# NOTE: continue-on-error: true はすべてのエラー(認証失敗・ネットワーク障害・コード品質違反を含む)を
# 無音で飲み込むため、このジョブはCIゲートとして機能しない。
# AGP 9.2.1 が Qodana でサポートされたら continue-on-error を削除すること。
# 対応状況は JetBrains YouTrack で "Qodana AGP 9.2" を検索して確認する。
continue-on-error: true
uses: JetBrains/qodana-action@d7b5ec2fbec32197ef447c450e00589ed5f34fd5 # v2026.1
with:
pr-mode: false
env:
QODANA_TOKEN: ${{ secrets.QODANA_TOKEN_693445069 }}
25 changes: 25 additions & 0 deletions .github/workflows/on-pull-request.yml
Original file line number Diff line number Diff line change
Expand Up @@ -127,3 +127,28 @@ jobs:
api-level: 36
arch: x86_64
script: ./gradlew connectedDebugAndroidTest

qodana:
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: write
checks: write

steps:
- uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.head.sha }} # to check out the actual pull request commit, not the merge commit
fetch-depth: 0 # a full history is required for pull request analysis

- name: 'Qodana Scan'
# NOTE: continue-on-error: true はすべてのエラー(認証失敗・ネットワーク障害・コード品質違反を含む)を
# 無音で飲み込むため、このジョブはCIゲートとして機能しない。
# AGP 9.2.1 が Qodana でサポートされたら continue-on-error を削除すること。
# 対応状況は JetBrains YouTrack で "Qodana AGP 9.2" を検索して確認する。
continue-on-error: true
uses: JetBrains/qodana-action@d7b5ec2fbec32197ef447c450e00589ed5f34fd5 # v2026.1
with:
pr-mode: true
env:
QODANA_TOKEN: ${{ secrets.QODANA_TOKEN_693445069 }}
36 changes: 0 additions & 36 deletions .github/workflows/qodana_code_quality.yml

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package kurou.androidpods.core.domain

import io.mockk.coVerify
import io.mockk.confirmVerified
import io.mockk.every
import io.mockk.mockk
import io.mockk.unmockkAll
import io.mockk.verify
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.test.runTest
import org.junit.After
import org.junit.Assert.assertEquals
import org.junit.Before
import org.junit.Test

class RssiThresholdUseCaseTest {
private lateinit var useCase: RssiThresholdUseCase
private val repository = mockk<RssiThresholdRepository>(relaxUnitFun = true)

@Before
fun setUp() {
useCase = RssiThresholdUseCase(repository)
}

@After
fun tearDown() {
unmockkAll()
}

@Test
fun `observeがrepositoryのobserveのFlowを返す`() =
runTest {
val fakeFlow = MutableStateFlow(RssiThreshold.VERY_NEAR)
every { repository.observe() } returns fakeFlow

val result = useCase.observe().first()

assertEquals(RssiThreshold.VERY_NEAR, result)
verify(exactly = 1) { repository.observe() }
confirmVerified(repository)
}

@Test
fun `updateでrepositoryのupdateが呼ばれる`() =
runTest {
useCase.update(RssiThreshold.NEAR)

coVerify(exactly = 1) { repository.update(RssiThreshold.NEAR) }
confirmVerified(repository)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,28 +18,21 @@ class BluetoothDeniedDialogTest {
val composeTestRule = createComposeRule()

@Test
fun `タイトルとメッセージが表示される`() {
fun `ダイアログが表示されて必要な文字列が全て表示されてOpen Settingsを押す`() {
var confirmed = false
composeTestRule.setContent {
BluetoothDeniedDialog(onDismiss = {}, onConfirm = {})
BluetoothDeniedDialog(onDismiss = {}, onConfirm = { confirmed = true })
}

composeTestRule.onNodeWithText("Bluetooth is Off").assertIsDisplayed()
composeTestRule.onNodeWithText("Bluetooth was not enabled. Please enable it in Settings.").assertIsDisplayed()
}

@Test
fun `Open SettingsボタンをタップするとonConfirmが呼ばれる`() {
var confirmed = false
composeTestRule.setContent {
BluetoothDeniedDialog(onDismiss = {}, onConfirm = { confirmed = true })
}

composeTestRule.onNodeWithText("Open Settings").performClick()
assertTrue(confirmed)
}

@Test
fun `CancelボタンをタップするとonDismissが呼ばれる`() {
fun `ダイアログが表示されてキャンセルを押す`() {
var dismissed = false
composeTestRule.setContent {
BluetoothDeniedDialog(onDismiss = { dismissed = true }, onConfirm = {})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,24 +18,17 @@ class BluetoothUnavailableDialogTest {
val composeTestRule = createComposeRule()

@Test
fun `タイトルとメッセージが表示される`() {
fun `ダイアログが表示されて必要な文字列が全て表示されてOKを押す`() {
var dismissed = false
composeTestRule.setContent {
BluetoothUnavailableDialog(onDismiss = {})
BluetoothUnavailableDialog(onDismiss = { dismissed = true })
}

composeTestRule.onNodeWithText("Bluetooth Unavailable").assertIsDisplayed()
composeTestRule
.onNodeWithText(
"This device does not have Bluetooth, so this app cannot be used.",
).assertIsDisplayed()
}

@Test
fun `OKボタンをタップするとonDismissが呼ばれる`() {
var dismissed = false
composeTestRule.setContent {
BluetoothUnavailableDialog(onDismiss = { dismissed = true })
}

composeTestRule.onNodeWithText("OK").performClick()
assertTrue(dismissed)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,31 +18,24 @@ class PermissionDeniedDialogTest {
val composeTestRule = createComposeRule()

@Test
fun `タイトルとメッセージが表示される`() {
fun `ダイアログが表示されて必要な文字列が全て表示されてOpen Settingsを押す`() {
var confirmed = false
composeTestRule.setContent {
PermissionDeniedDialog(onDismiss = {}, onConfirm = {})
PermissionDeniedDialog(onDismiss = {}, onConfirm = { confirmed = true })
}

composeTestRule.onNodeWithText("Permission Required").assertIsDisplayed()
composeTestRule
.onNodeWithText(
"Bluetooth permissions were denied. Please grant them in Settings.",
).assertIsDisplayed()
}

@Test
fun `Open SettingsボタンをタップするとonConfirmが呼ばれる`() {
var confirmed = false
composeTestRule.setContent {
PermissionDeniedDialog(onDismiss = {}, onConfirm = { confirmed = true })
}

composeTestRule.onNodeWithText("Open Settings").performClick()
assertTrue(confirmed)
}

@Test
fun `CancelボタンをタップするとonDismissが呼ばれる`() {
fun `ダイアログが表示されてキャンセルを押す`() {
var dismissed = false
composeTestRule.setContent {
PermissionDeniedDialog(onDismiss = { dismissed = true }, onConfirm = {})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,37 +20,26 @@ class OverlayPositionDialogTest {
val composeTestRule = createComposeRule()

@Test
fun `タイトルと選択肢が表示される`() {
fun `ダイアログが表示されて必要な文字列が全て表示されていずれかを選択する`() {
var selected: OverlayPosition? = null
composeTestRule.setContent {
OverlayPositionDialog(
currentPosition = OverlayPosition.TOP,
onDismiss = {},
onPositionSelected = {},
onPositionSelected = { selected = it },
)
}

composeTestRule.onNodeWithText("Overlay position").assertIsDisplayed()
composeTestRule.onNodeWithText("Top").assertIsDisplayed()
composeTestRule.onNodeWithText("Bottom").assertIsDisplayed()
}

@Test
fun `選択肢をタップするとonPositionSelectedが呼ばれる`() {
var selected: OverlayPosition? = null
composeTestRule.setContent {
OverlayPositionDialog(
currentPosition = OverlayPosition.TOP,
onDismiss = {},
onPositionSelected = { selected = it },
)
}

composeTestRule.onNodeWithText("Bottom").performClick()
assertEquals(OverlayPosition.BOTTOM, selected)
}

@Test
fun `CancelボタンをタップするとonDismissが呼ばれる`() {
fun `ダイアログが表示されてキャンセルを押す`() {
var dismissed = false
composeTestRule.setContent {
OverlayPositionDialog(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,31 +18,24 @@ class PermissionRequiredDialogTest {
val composeTestRule = createComposeRule()

@Test
fun `タイトルとメッセージが表示される`() {
fun `ダイアログが表示されて必要な文字列が全て表示されてOpen Settingsを押す`() {
var confirmed = false
composeTestRule.setContent {
PermissionRequiredDialog(onDismiss = {}, onConfirm = {})
PermissionRequiredDialog(onDismiss = {}, onConfirm = { confirmed = true })
}

composeTestRule.onNodeWithText("Bluetooth Permission Required").assertIsDisplayed()
composeTestRule
.onNodeWithText(
"This app requires Bluetooth permissions to connect to devices. Please grant permissions in Settings.",
).assertIsDisplayed()
}

@Test
fun `Open SettingsボタンをタップするとonConfirmが呼ばれる`() {
var confirmed = false
composeTestRule.setContent {
PermissionRequiredDialog(onDismiss = {}, onConfirm = { confirmed = true })
}

composeTestRule.onNodeWithText("Open Settings").performClick()
assertTrue(confirmed)
}

@Test
fun `CancelボタンをタップするとonDismissが呼ばれる`() {
fun `ダイアログが表示されてキャンセルを押す`() {
var dismissed = false
composeTestRule.setContent {
PermissionRequiredDialog(onDismiss = { dismissed = true }, onConfirm = {})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import androidx.compose.ui.test.onNodeWithText
import androidx.compose.ui.test.performClick
import kurou.androidpods.core.domain.RssiThreshold
import org.junit.Assert.assertEquals
import org.junit.Assert.assertTrue
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
Expand All @@ -19,12 +20,13 @@ class RssiThresholdDialogTest {
val composeTestRule = createComposeRule()

@Test
fun `タイトルと選択肢が表示される`() {
fun `ダイアログが表示されて必要な文字列が全て表示されていずれかを選択する`() {
var selected: RssiThreshold? = null
composeTestRule.setContent {
RssiThresholdDialog(
currentThreshold = RssiThreshold.VERY_NEAR,
onDismiss = {},
onThresholdSelected = {},
onThresholdSelected = { selected = it },
)
}

Expand All @@ -33,20 +35,23 @@ class RssiThresholdDialogTest {
composeTestRule.onNodeWithText("Medium range (~5m)").assertIsDisplayed()
composeTestRule.onNodeWithText("Near range (~2m)").assertIsDisplayed()
composeTestRule.onNodeWithText("Very near (~1m)").assertIsDisplayed()

composeTestRule.onNodeWithText("All devices").performClick()
assertEquals(RssiThreshold.ALL, selected)
}

@Test
fun `選択肢をタップするとonThresholdSelectedが呼ばれる`() {
var selected: RssiThreshold? = null
fun `ダイアログが表示されてキャンセルを押す`() {
var dismissed = false
composeTestRule.setContent {
RssiThresholdDialog(
currentThreshold = RssiThreshold.VERY_NEAR,
onDismiss = {},
onThresholdSelected = { selected = it },
onDismiss = { dismissed = true },
onThresholdSelected = {},
)
}

composeTestRule.onNodeWithText("All devices").performClick()
assertEquals(RssiThreshold.ALL, selected)
composeTestRule.onNodeWithText("Cancel").performClick()
assertTrue(dismissed)
}
}
Loading
Loading