Fix Playback Reporting, Add QueueProvider Test Coverage (#300)

Addresses regression where playback was not reported
This commit is contained in:
Violet Caulfield
2025-04-28 19:45:47 -05:00
committed by GitHub
parent 5f2d2a2f94
commit 2160fbc854
6 changed files with 186 additions and 189 deletions

View File

@@ -2,12 +2,8 @@ import 'react-native'
import React from 'react'
import App from '../App'
// Note: import explicitly to use the types shipped with jest.
import { it } from '@jest/globals'
import { render } from '@testing-library/react-native'
// Note: test renderer must be required after react-native.
import renderer from 'react-test-renderer'
it('renders correctly', () => {
renderer.create(<App />)
test(`${App.name} renders successfully`, () => {
render(<App />)
})

View File

@@ -1,23 +1,25 @@
import 'react-native'
import React from 'react'
import { render, screen, waitFor } from '@testing-library/react-native'
import { act, fireEvent, render, screen, waitFor } from '@testing-library/react-native'
import TrackPlayer, { Event } from 'react-native-track-player'
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
import { QueueProvider, useQueueContext } from '../player/queue-provider'
import { Button, Text } from 'react-native'
import { Event } from 'react-native-track-player'
import { QueueProvider, useQueueContext } from '../player/queue-provider'
import { eventHandler } from './setup-rntp'
const queryClient = new QueryClient()
const QueueConsumer = () => {
const { currentIndex, useSkip } = useQueueContext()
const { currentIndex, useSkip, usePrevious } = useQueueContext()
return (
<>
<Text testID='current-index'>{currentIndex}</Text>
<Button title='skip' testID='use-skip' onPress={() => useSkip.mutate(undefined)} />
<Button title='previous' testID='use-previous' onPress={() => usePrevious.mutate()} />
</>
)
}
@@ -35,13 +37,27 @@ test(`${QueueProvider.name} renders and functions correctly`, async () => {
expect(currentIndex.props.children).toBe(-1)
eventHandler({
type: Event.PlaybackActiveTrackChanged,
index: 3,
act(() => {
eventHandler({
type: Event.PlaybackActiveTrackChanged,
index: 3,
})
})
await waitFor(() => {
const updatedIndex = screen.getByTestId('current-index')
expect(updatedIndex.props.children).toBe(3)
})
act(() => {
eventHandler({
type: Event.PlaybackActiveTrackChanged,
index: 2,
})
})
await waitFor(() => {
const updatedIndex = screen.getByTestId('current-index')
expect(updatedIndex.props.children).toBe(2)
})
})

View File

@@ -1,58 +1,54 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import * as TrackPlayer from 'react-native-track-player'
export let eventHandler: any
// https://github.com/doublesymmetry/react-native-track-player/issues/501
jest.mock('react-native-track-player', () => {
const listeners = new Map()
return {
__esModule: true,
default: {
addEventListener: () => ({
remove: jest.fn(),
}),
registerEventHandler: jest.fn(),
registerPlaybackService: jest.fn(),
setupPlayer: jest.fn().mockResolvedValue(undefined),
destroy: jest.fn(),
updateOptions: jest.fn(),
reset: jest.fn(),
add: jest.fn(),
addEventListener: () => ({
remove: jest.fn(),
skip: jest.fn(),
skipToNext: jest.fn(),
skipToPrevious: jest.fn(),
removeUpcomingTracks: jest.fn(),
// playback commands
play: jest.fn(),
pause: jest.fn(),
stop: jest.fn(),
seekTo: jest.fn(),
setVolume: jest.fn(),
setRate: jest.fn(),
// player getters
getQueue: jest.fn(),
getTrack: jest.fn(),
getActiveTrackIndex: jest.fn(),
getActiveTrack: jest.fn(),
getCurrentTrack: jest.fn(),
getVolume: jest.fn(),
getDuration: jest.fn(),
getPosition: jest.fn(),
getBufferedPosition: jest.fn(),
getState: jest.fn(),
getRate: jest.fn(),
},
}),
registerEventHandler: jest.fn(),
registerPlaybackService: jest.fn(),
setupPlayer: jest.fn().mockResolvedValue(undefined),
destroy: jest.fn(),
updateOptions: jest.fn(),
reset: jest.fn(),
add: jest.fn(),
remove: jest.fn(),
skip: jest.fn(),
skipToNext: jest.fn(),
skipToPrevious: jest.fn(),
removeUpcomingTracks: jest.fn(),
// playback commands
play: jest.fn(),
pause: jest.fn(),
stop: jest.fn(),
seekTo: jest.fn(),
setVolume: jest.fn(),
setRate: jest.fn(),
// player getters
getQueue: jest.fn(),
getTrack: jest.fn(),
getActiveTrackIndex: jest.fn(),
getActiveTrack: jest.fn(),
getCurrentTrack: jest.fn(),
getVolume: jest.fn(),
getDuration: jest.fn(),
getProgress: jest.fn().mockResolvedValue({ position: 0 }),
getBufferedPosition: jest.fn(),
getState: jest.fn(),
getRate: jest.fn(),
useProgress: () => ({
position: 100,
position: 0,
buffered: 150,
duration: 200,
}),
usePlaybackState: () => 'playing',
// eslint-disable @typescript-eslint/no-explicit-any
useTrackPlayerEvents: (events: TrackPlayer.Event[], handler: (variables: any) => void) => {
useTrackPlayerEvents: (events: Event[], handler: (variables: any) => void) => {
eventHandler = handler
},
Capability: {
@@ -100,13 +96,3 @@ jest.mock('react-native-track-player', () => {
},
}
})
export let eventHandler: any
beforeEach(() => {
const player = TrackPlayer as any
player.useTrackPlayerEvents = (events: Event[], handler: (variables: any) => void) => {
eventHandler = handler
}
})

View File

@@ -1,123 +1,123 @@
{
"name": "jellify",
"version": "0.11.13",
"private": true,
"scripts": {
"init:ios": "yarn install && yarn pod:install",
"reinstall": "rm -rf ./node_modules && yarn install",
"android": "react-native run-android",
"ios": "react-native run-ios",
"lint": "eslint .",
"start": "react-native start",
"test": "jest",
"clean:ios": "cd ios && pod deintegrate",
"clean:android": "cd android && rm -rf app/ build/",
"pod:install": "cd ios && bundle install && RCT_NEW_ARCH_ENABLED=0 bundle exec pod install",
"pod:install-new-arch": "cd ios && bundle install && RCT_NEW_ARCH_ENABLED=1 bundle exec pod install",
"fastlane:ios:build": "cd ios && bundle exec fastlane build",
"fastlane:ios:match": "cd ios && bundle exec fastlane match development",
"fastlane:ios:beta": "cd ios && bundle exec fastlane beta",
"fastlane:android:build": "cd android && bundle install && bundle exec fastlane build",
"androidBuild": "cd android && ./gradlew clean && ./gradlew assembleRelease && cd .. && echo 'find apk in android/app/build/outputs/apk/release'",
"prepare": "husky",
"format:check": "prettier --check .",
"format": "prettier --write .",
"postinstall": "patch-package"
},
"dependencies": {
"@jellyfin/sdk": "^0.11.0",
"@react-native-community/blur": "^4.4.1",
"@react-native-community/cli": "^15.1.3",
"@react-native-community/netinfo": "^11.4.1",
"@react-native-masked-view/masked-view": "^0.3.2",
"@react-navigation/bottom-tabs": "^7.3.10",
"@react-navigation/material-top-tabs": "^7.2.10",
"@react-navigation/native": "^7.1.6",
"@react-navigation/native-stack": "^7.3.10",
"@react-navigation/stack": "^7.2.10",
"@tamagui/config": "^1.126.1",
"@tamagui/toast": "^1.126.1",
"@tanstack/query-sync-storage-persister": "^5.74.6",
"@tanstack/react-query": "^5.74.4",
"@tanstack/react-query-persist-client": "^5.74.6",
"@testing-library/react-native": "^13.2.0",
"axios": "^1.8.4",
"bundle": "^2.1.0",
"burnt": "^0.13.0",
"expo": "^52.0.46",
"expo-image": "^2.0.7",
"gem": "^2.4.3",
"invert-color": "^2.0.0",
"jest-expo": "^52.0.6",
"lodash": "^4.17.21",
"npm-bundle": "^3.0.3",
"patch-package": "^8.0.0",
"react": "18.3.1",
"react-freeze": "^1.0.4",
"react-native": "0.77.0",
"react-native-background-actions": "^4.0.1",
"react-native-blurhash": "^2.1.1",
"react-native-boost": "^0.5.6",
"react-native-carplay": "^2.4.1-beta.0",
"react-native-device-info": "^14.0.4",
"react-native-draggable-flatlist": "^4.0.2",
"react-native-fs": "^2.20.0",
"react-native-gesture-handler": "^2.25.0",
"react-native-haptic-feedback": "^2.3.3",
"react-native-mmkv": "^2.12.2",
"react-native-pager-view": "^6.7.1",
"react-native-reanimated": "^3.17.5",
"react-native-safe-area-context": "^5.4.0",
"react-native-screens": "^4.10.0",
"react-native-swipeable-item": "^2.0.9",
"react-native-text-ticker": "^1.14.0",
"react-native-track-player": "^4.1.1",
"react-native-url-polyfill": "^2.0.0",
"react-native-uuid": "^2.0.3",
"react-native-vector-icons": "^10.2.0",
"ruby": "^0.6.1",
"tamagui": "^1.126.1"
},
"devDependencies": {
"@babel/core": "^7.25.2",
"@babel/preset-env": "^7.25.3",
"@babel/runtime": "^7.25.0",
"@react-native-community/cli-platform-android": "15.1.3",
"@react-native-community/cli-platform-ios": "15.1.3",
"@react-native/babel-preset": "0.77.0",
"@react-native/eslint-config": "0.77.0",
"@react-native/metro-config": "0.77.0",
"@react-native/typescript-config": "0.77.0",
"@types/jest": "^29.5.13",
"@types/lodash": "^4.17.10",
"@types/react": "^18.2.6",
"@types/react-native-vector-icons": "^6.4.18",
"@types/react-test-renderer": "^18.3.1",
"@typescript-eslint/eslint-plugin": "^8.29.1",
"@typescript-eslint/parser": "^8.29.1",
"babel-plugin-module-resolver": "^5.0.2",
"eslint": "^8.57.1",
"eslint-config-prettier": "^10.1.2",
"eslint-plugin-import": "^2.31.0",
"eslint-plugin-prettier": "^5.2.6",
"eslint-plugin-react": "^7.37.5",
"eslint-plugin-react-native": "^5.0.0",
"husky": "^9.1.7",
"jest": "^29.6.3",
"jscodeshift": "^0.15.2",
"lint-staged": "^15.5.0",
"prettier": "^2.8.8",
"react-native-cli-bump-version": "^1.5.1",
"react-test-renderer": "18.3.1",
"typescript": "5.7.3"
},
"lint-staged": {
"*.{js,jsx,ts,tsx}": [
"prettier --write",
"eslint --fix"
]
},
"engines": {
"node": ">=18"
}
}
"name": "jellify",
"version": "0.11.13",
"private": true,
"scripts": {
"init:ios": "yarn install && yarn pod:install",
"reinstall": "rm -rf ./node_modules && yarn install",
"android": "react-native run-android",
"ios": "react-native run-ios",
"lint": "eslint .",
"start": "react-native start",
"test": "jest",
"clean:ios": "cd ios && pod deintegrate",
"clean:android": "cd android && rm -rf app/ build/",
"pod:install": "cd ios && bundle install && RCT_NEW_ARCH_ENABLED=0 bundle exec pod install",
"pod:install-new-arch": "cd ios && bundle install && RCT_NEW_ARCH_ENABLED=1 bundle exec pod install",
"fastlane:ios:build": "cd ios && bundle exec fastlane build",
"fastlane:ios:match": "cd ios && bundle exec fastlane match development",
"fastlane:ios:beta": "cd ios && bundle exec fastlane beta",
"fastlane:android:build": "cd android && bundle install && bundle exec fastlane build",
"androidBuild": "cd android && ./gradlew clean && ./gradlew assembleRelease && cd .. && echo 'find apk in android/app/build/outputs/apk/release'",
"prepare": "husky",
"format:check": "prettier --check .",
"format": "prettier --write .",
"postinstall": "patch-package"
},
"dependencies": {
"@jellyfin/sdk": "^0.11.0",
"@react-native-community/blur": "^4.4.1",
"@react-native-community/cli": "^15.1.3",
"@react-native-community/netinfo": "^11.4.1",
"@react-native-masked-view/masked-view": "^0.3.2",
"@react-navigation/bottom-tabs": "^7.3.10",
"@react-navigation/material-top-tabs": "^7.2.10",
"@react-navigation/native": "^7.1.6",
"@react-navigation/native-stack": "^7.3.10",
"@react-navigation/stack": "^7.2.10",
"@tamagui/config": "^1.126.1",
"@tamagui/toast": "^1.126.1",
"@tanstack/query-sync-storage-persister": "^5.74.6",
"@tanstack/react-query": "^5.74.4",
"@tanstack/react-query-persist-client": "^5.74.6",
"@testing-library/react-native": "^13.2.0",
"axios": "^1.8.4",
"bundle": "^2.1.0",
"burnt": "^0.13.0",
"expo": "^52.0.46",
"expo-image": "^2.0.7",
"gem": "^2.4.3",
"invert-color": "^2.0.0",
"jest-expo": "^52.0.6",
"lodash": "^4.17.21",
"npm-bundle": "^3.0.3",
"patch-package": "^8.0.0",
"react": "18.3.1",
"react-freeze": "^1.0.4",
"react-native": "0.77.0",
"react-native-background-actions": "^4.0.1",
"react-native-blurhash": "^2.1.1",
"react-native-boost": "^0.5.6",
"react-native-carplay": "^2.4.1-beta.0",
"react-native-device-info": "^14.0.4",
"react-native-draggable-flatlist": "^4.0.2",
"react-native-fs": "^2.20.0",
"react-native-gesture-handler": "^2.25.0",
"react-native-haptic-feedback": "^2.3.3",
"react-native-mmkv": "^2.12.2",
"react-native-pager-view": "^6.7.1",
"react-native-reanimated": "^3.17.5",
"react-native-safe-area-context": "^5.4.0",
"react-native-screens": "^4.10.0",
"react-native-swipeable-item": "^2.0.9",
"react-native-text-ticker": "^1.14.0",
"react-native-track-player": "^4.1.1",
"react-native-url-polyfill": "^2.0.0",
"react-native-uuid": "^2.0.3",
"react-native-vector-icons": "^10.2.0",
"ruby": "^0.6.1",
"tamagui": "^1.126.1"
},
"devDependencies": {
"@babel/core": "^7.25.2",
"@babel/preset-env": "^7.25.3",
"@babel/runtime": "^7.25.0",
"@react-native-community/cli-platform-android": "15.1.3",
"@react-native-community/cli-platform-ios": "15.1.3",
"@react-native/babel-preset": "0.77.0",
"@react-native/eslint-config": "0.77.0",
"@react-native/metro-config": "0.77.0",
"@react-native/typescript-config": "0.77.0",
"@types/jest": "^29.5.13",
"@types/lodash": "^4.17.10",
"@types/react": "^18.2.6",
"@types/react-native-vector-icons": "^6.4.18",
"@types/react-test-renderer": "^18.3.1",
"@typescript-eslint/eslint-plugin": "^8.29.1",
"@typescript-eslint/parser": "^8.29.1",
"babel-plugin-module-resolver": "^5.0.2",
"eslint": "^8.57.1",
"eslint-config-prettier": "^10.1.2",
"eslint-plugin-import": "^2.31.0",
"eslint-plugin-prettier": "^5.2.6",
"eslint-plugin-react": "^7.37.5",
"eslint-plugin-react-native": "^5.0.0",
"husky": "^9.1.7",
"jest": "^29.6.3",
"jscodeshift": "^0.15.2",
"lint-staged": "^15.5.0",
"prettier": "^2.8.8",
"react-native-cli-bump-version": "^1.5.1",
"react-test-renderer": "18.3.1",
"typescript": "5.7.3"
},
"lint-staged": {
"*.{js,jsx,ts,tsx}": [
"prettier --write",
"eslint --fix"
]
},
"engines": {
"node": ">=18"
}
}

View File

@@ -34,7 +34,7 @@ const PlayerContextInitializer = () => {
let playStateApi: PlaystateApi | undefined
if (Client.api) getPlaystateApi(Client.api)
if (Client.api) playStateApi = getPlaystateApi(Client.api)
//#region State
const [nowPlaying, setNowPlaying] = useState<JellifyTrack | undefined>(

View File

@@ -157,7 +157,6 @@ const QueueContextInitailizer = () => {
const previous = async () => {
trigger('impactMedium')
setCurrentIndex(-1)
const { position } = await TrackPlayer.getProgress()
console.debug(