mirror of
https://github.com/ajnart/homarr.git
synced 2025-11-10 15:35:55 +01:00
🧪 Add test for Plex
This commit is contained in:
@@ -37,5 +37,6 @@ module.exports = {
|
|||||||
max: 3,
|
max: 3,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
'testing-library/no-node-access': ['error', { allowContainerFirstChild: true }],
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -99,7 +99,8 @@
|
|||||||
"turbo": "^1.7.4",
|
"turbo": "^1.7.4",
|
||||||
"typescript": "^4.7.4",
|
"typescript": "^4.7.4",
|
||||||
"video.js": "^8.0.3",
|
"video.js": "^8.0.3",
|
||||||
"vitest": "^0.29.3"
|
"vitest": "^0.29.3",
|
||||||
|
"vitest-fetch-mock": "^0.2.2"
|
||||||
},
|
},
|
||||||
"resolutions": {
|
"resolutions": {
|
||||||
"@types/react": "17.0.2",
|
"@types/react": "17.0.2",
|
||||||
|
|||||||
8
setupVitest.ts
Normal file
8
setupVitest.ts
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
//setupVitest.js or similar file
|
||||||
|
import createFetchMock from 'vitest-fetch-mock';
|
||||||
|
import { vi } from 'vitest';
|
||||||
|
|
||||||
|
const fetchMocker = createFetchMock(vi);
|
||||||
|
|
||||||
|
// sets globalThis.fetch and globalThis.fetchMock to our mocked version
|
||||||
|
fetchMocker.enableMocks();
|
||||||
@@ -2,15 +2,51 @@ import { render } from '@testing-library/react';
|
|||||||
import { describe, expect, it } from 'vitest';
|
import { describe, expect, it } from 'vitest';
|
||||||
import { AppAvatar } from './AppAvatar';
|
import { AppAvatar } from './AppAvatar';
|
||||||
|
|
||||||
describe(AppAvatar.name, () => {
|
describe('AppAvatar', () => {
|
||||||
it.concurrent('display placeholder when no url', () => {
|
it('display placeholder when no url', () => {
|
||||||
const { container } = render(<AppAvatar iconUrl="" color="blue" />);
|
const { container } = render(<AppAvatar iconUrl="" color="blue" />);
|
||||||
|
|
||||||
expect(container.firstElementChild).not.toBeNull();
|
expect(container.firstChild).toMatchInlineSnapshot(`
|
||||||
expect(container.firstElementChild!.className).contain('mantine-Avatar-root');
|
<div
|
||||||
|
class="mantine-Avatar-root mantine-1rb4n7x"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="mantine-lgdaxf mantine-Avatar-placeholder"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
class="mantine-rsrrdl mantine-Avatar-placeholderIcon"
|
||||||
|
fill="none"
|
||||||
|
height="15"
|
||||||
|
viewBox="0 0 15 15"
|
||||||
|
width="15"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
clip-rule="evenodd"
|
||||||
|
d="M0.877014 7.49988C0.877014 3.84219 3.84216 0.877045 7.49985 0.877045C11.1575 0.877045 14.1227 3.84219 14.1227 7.49988C14.1227 11.1575 11.1575 14.1227 7.49985 14.1227C3.84216 14.1227 0.877014 11.1575 0.877014 7.49988ZM7.49985 1.82704C4.36683 1.82704 1.82701 4.36686 1.82701 7.49988C1.82701 8.97196 2.38774 10.3131 3.30727 11.3213C4.19074 9.94119 5.73818 9.02499 7.50023 9.02499C9.26206 9.02499 10.8093 9.94097 11.6929 11.3208C12.6121 10.3127 13.1727 8.97172 13.1727 7.49988C13.1727 4.36686 10.6328 1.82704 7.49985 1.82704ZM10.9818 11.9787C10.2839 10.7795 8.9857 9.97499 7.50023 9.97499C6.01458 9.97499 4.71624 10.7797 4.01845 11.9791C4.97952 12.7272 6.18765 13.1727 7.49985 13.1727C8.81227 13.1727 10.0206 12.727 10.9818 11.9787ZM5.14999 6.50487C5.14999 5.207 6.20212 4.15487 7.49999 4.15487C8.79786 4.15487 9.84999 5.207 9.84999 6.50487C9.84999 7.80274 8.79786 8.85487 7.49999 8.85487C6.20212 8.85487 5.14999 7.80274 5.14999 6.50487ZM7.49999 5.10487C6.72679 5.10487 6.09999 5.73167 6.09999 6.50487C6.09999 7.27807 6.72679 7.90487 7.49999 7.90487C8.27319 7.90487 8.89999 7.27807 8.89999 6.50487C8.89999 5.73167 8.27319 5.10487 7.49999 5.10487Z"
|
||||||
|
fill="currentColor"
|
||||||
|
fill-rule="evenodd"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
|
||||||
const svgElement = container.querySelector('svg');
|
it('display placeholder when valid url', () => {
|
||||||
expect(svgElement).not.toBeNull();
|
const { container } = render(
|
||||||
expect(svgElement?.getAttribute('fill')).not.toBeNull();
|
<AppAvatar iconUrl="https://homarr.dev/img/logo.svg" color="red" />
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(container.firstChild).toMatchInlineSnapshot(`
|
||||||
|
<div
|
||||||
|
class="mantine-Avatar-root mantine-11ss4u9"
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
class="mantine-1trwvlz mantine-Avatar-image"
|
||||||
|
src="https://homarr.dev/img/logo.svg"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
`);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ export const getServerSideTranslations = async (
|
|||||||
res?: ServerResponse,
|
res?: ServerResponse,
|
||||||
) => {
|
) => {
|
||||||
if (!req || !res) {
|
if (!req || !res) {
|
||||||
return await serverSideTranslations(
|
return serverSideTranslations(
|
||||||
requestLocale ?? 'en',
|
requestLocale ?? 'en',
|
||||||
namespaces
|
namespaces
|
||||||
);
|
);
|
||||||
@@ -17,7 +17,7 @@ export const getServerSideTranslations = async (
|
|||||||
|
|
||||||
const configLocale = getCookie('config-locale', { req, res });
|
const configLocale = getCookie('config-locale', { req, res });
|
||||||
|
|
||||||
return await serverSideTranslations(
|
return serverSideTranslations(
|
||||||
(configLocale ?? requestLocale ?? 'en') as string,
|
(configLocale ?? requestLocale ?? 'en') as string,
|
||||||
namespaces
|
namespaces
|
||||||
);
|
);
|
||||||
|
|||||||
73
src/tools/server/sdk/plex/plexClient.test.ts
Normal file
73
src/tools/server/sdk/plex/plexClient.test.ts
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
import { describe, expect, it } from 'vitest';
|
||||||
|
import 'vitest-fetch-mock';
|
||||||
|
import { PlexClient } from './plexClient';
|
||||||
|
|
||||||
|
const mockResponse = `<MediaContainer size="1">
|
||||||
|
<Video addedAt="0000000" art="/library/metadata/2/art/00000000" audienceRating="0.0" audienceRatingImage="niceImage" chapterSource="media" contentRating="TV-PG" duration="6262249" guid="plex://movie/0000000000000000" key="/library/metadata/2" lastViewedAt="0000000" librarySectionID="1" librarySectionKey="/library/sections/1" librarySectionTitle="Movies" originalTitle="00000000000000" originallyAvailableAt="0000-00-00" rating="0.0" ratingImage="ratingimage" ratingKey="2" sessionKey="1" studio="Example Studio" summary="Lorem Ispum dolor sit amet" tagline="Yep" thumb="/library/metadata/2/thumb/0000000" title="A long title" titleSort="A short title" type="movie" updatedAt="000000" viewOffset="0" year="0000">
|
||||||
|
<Media audioProfile="ma" id="2" videoProfile="high" audioChannels="2" audioCodec="aac" bitrate="20231" container="mp4" duration="6262249" height="1080" optimizedForStreaming="1" protocol="dash" videoCodec="h264" videoFrameRate="24p" videoResolution="1080p" width="1920" selected="1">
|
||||||
|
<Part audioProfile="ma" hasThumbnail="1" id="2" videoProfile="high" bitrate="20231" container="mp4" duration="6262249" height="1080" optimizedForStreaming="1" protocol="dash" width="1920" decision="transcode" selected="1">
|
||||||
|
<Stream bitDepth="8" bitrate="19975" chromaLocation="left" chromaSubsampling="4:2:0" codec="h264" codedHeight="1088" codedWidth="1920" default="1" displayTitle="XXXX" extendedDisplayTitle="Yes" frameRate="23.975999832153320" hasScalingMatrix="0" height="1080" id="4" level="41" profile="high" refFrames="4" scanType="progressive" streamType="1" title="Example" width="1920" decision="copy" location="segments-video"/>
|
||||||
|
<Stream bitrate="256" bitrateMode="cbr" channels="2" codec="aac" default="1" displayTitle="Not Existing" extendedDisplayTitle="Yes, really" id="5" language="Yep" languageCode="jpn" languageTag="ch" selected="1" streamType="2" decision="transcode" location="segments-audio"/>
|
||||||
|
</Part>
|
||||||
|
</Media>
|
||||||
|
<Genre count="13" filter="genre=48" id="48" tag="Drama"/>
|
||||||
|
<Genre count="8" filter="genre=104" id="104" tag="Adventure"/>
|
||||||
|
<User id="1" thumb="https://google.com" title="example_usr"/>
|
||||||
|
<Player address="0.0.0.0" device="Windows" machineIdentifier="72483785378573857385" model="bundled" platform="Chrome" platformVersion="111.0" product="Plex Web" profile="Web" state="paused" title="Chrome" version="0.000.0" local="1" relayed="0" secure="1" userID="1"/>
|
||||||
|
<Session id="2894294r2jf2038fj3098jgf3gt" bandwidth="21560" location="lan"/>
|
||||||
|
<TranscodeSession key="/transcode/sessions/example-session" throttled="0" complete="0" progress="0" size="-22" speed="18.600000381469727" error="0" duration="100" remaining="70" context="streaming" sourceVideoCodec="h264" sourceAudioCodec="dca" videoDecision="copy" audioDecision="transcode" protocol="dash" container="mp4" videoCodec="h264" audioCodec="aac" audioChannels="2" width="1920" height="1080" transcodeHwRequested="0" transcodeHwFullPipeline="0" timeStamp="1679349635.2791338" maxOffsetAvailable="104.27" minOffsetAvailable="84.166999816894531"/>
|
||||||
|
</Video>
|
||||||
|
</MediaContainer>`;
|
||||||
|
|
||||||
|
describe('Plex SDK', () => {
|
||||||
|
it('abc', async () => {
|
||||||
|
// arrange
|
||||||
|
const client = new PlexClient('https://plex', 'MY_TOKEN');
|
||||||
|
|
||||||
|
fetchMock.mockResponseOnce(mockResponse);
|
||||||
|
|
||||||
|
// act
|
||||||
|
const response = await client.getSessions();
|
||||||
|
|
||||||
|
// assert
|
||||||
|
expect(fetchMock.requests().length).toBe(1);
|
||||||
|
expect(fetchMock.requests()[0].url).toBe('https://plex/status/sessions?X-Plex-Token=MY_TOKEN');
|
||||||
|
expect(response).not.toBeNull();
|
||||||
|
expect(response.length).toBe(1);
|
||||||
|
expect(response[0].id).toBe('2894294r2jf2038fj3098jgf3gt');
|
||||||
|
expect(response[0].username).toBe('example_usr');
|
||||||
|
expect(response[0].userProfilePicture).toBe('https://google.com');
|
||||||
|
expect(response[0].sessionName).toBe('Plex Web (Chrome)');
|
||||||
|
expect(response[0].currentlyPlaying).toMatchObject({
|
||||||
|
name: 'A long title',
|
||||||
|
type: 'movie',
|
||||||
|
metadata: {
|
||||||
|
video: {
|
||||||
|
bitrate: '20231',
|
||||||
|
height: '1080',
|
||||||
|
videoCodec: 'h264',
|
||||||
|
videoFrameRate: '24p',
|
||||||
|
width: '1920',
|
||||||
|
},
|
||||||
|
audio: { audioChannels: '2', audioCodec: 'aac' },
|
||||||
|
transcoding: {
|
||||||
|
audioChannels: '2',
|
||||||
|
audioCodec: 'aac',
|
||||||
|
audioDecision: 'transcode',
|
||||||
|
container: 'mp4',
|
||||||
|
context: 'streaming',
|
||||||
|
duration: '100',
|
||||||
|
error: false,
|
||||||
|
height: '1080',
|
||||||
|
sourceAudioCodec: 'dca',
|
||||||
|
sourceVideoCodec: 'h264',
|
||||||
|
timeStamp: '1679349635.2791338',
|
||||||
|
transcodeHwRequested: false,
|
||||||
|
videoCodec: 'h264',
|
||||||
|
videoDecision: 'copy',
|
||||||
|
width: '1920',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -12,6 +12,9 @@ export default defineConfig({
|
|||||||
reporter: ['html'],
|
reporter: ['html'],
|
||||||
all: true,
|
all: true,
|
||||||
exclude: ['.next/', '.yarn/', 'data/']
|
exclude: ['.next/', '.yarn/', 'data/']
|
||||||
}
|
},
|
||||||
|
setupFiles: [
|
||||||
|
"./setupVitest.ts"
|
||||||
|
]
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
59
yarn.lock
59
yarn.lock
@@ -3145,6 +3145,15 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"cross-fetch@npm:^3.0.6":
|
||||||
|
version: 3.1.5
|
||||||
|
resolution: "cross-fetch@npm:3.1.5"
|
||||||
|
dependencies:
|
||||||
|
node-fetch: 2.6.7
|
||||||
|
checksum: f6b8c6ee3ef993ace6277fd789c71b6acf1b504fd5f5c7128df4ef2f125a429e29cd62dc8c127523f04a5f2fa4771ed80e3f3d9695617f441425045f505cf3bb
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"cross-spawn@npm:^7.0.0, cross-spawn@npm:^7.0.2":
|
"cross-spawn@npm:^7.0.0, cross-spawn@npm:^7.0.2":
|
||||||
version: 7.0.3
|
version: 7.0.3
|
||||||
resolution: "cross-spawn@npm:7.0.3"
|
resolution: "cross-spawn@npm:7.0.3"
|
||||||
@@ -4932,6 +4941,7 @@ __metadata:
|
|||||||
uuid: ^8.3.2
|
uuid: ^8.3.2
|
||||||
video.js: ^8.0.3
|
video.js: ^8.0.3
|
||||||
vitest: ^0.29.3
|
vitest: ^0.29.3
|
||||||
|
vitest-fetch-mock: ^0.2.2
|
||||||
xml-js: ^1.6.11
|
xml-js: ^1.6.11
|
||||||
yarn: ^1.22.19
|
yarn: ^1.22.19
|
||||||
zustand: ^4.1.4
|
zustand: ^4.1.4
|
||||||
@@ -6273,6 +6283,20 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"node-fetch@npm:2.6.7":
|
||||||
|
version: 2.6.7
|
||||||
|
resolution: "node-fetch@npm:2.6.7"
|
||||||
|
dependencies:
|
||||||
|
whatwg-url: ^5.0.0
|
||||||
|
peerDependencies:
|
||||||
|
encoding: ^0.1.0
|
||||||
|
peerDependenciesMeta:
|
||||||
|
encoding:
|
||||||
|
optional: true
|
||||||
|
checksum: 8d816ffd1ee22cab8301c7756ef04f3437f18dace86a1dae22cf81db8ef29c0bf6655f3215cb0cdb22b420b6fe141e64b26905e7f33f9377a7fa59135ea3e10b
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"node-gyp@npm:latest":
|
"node-gyp@npm:latest":
|
||||||
version: 9.3.1
|
version: 9.3.1
|
||||||
resolution: "node-gyp@npm:9.3.1"
|
resolution: "node-gyp@npm:9.3.1"
|
||||||
@@ -7868,6 +7892,13 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"tr46@npm:~0.0.3":
|
||||||
|
version: 0.0.3
|
||||||
|
resolution: "tr46@npm:0.0.3"
|
||||||
|
checksum: 726321c5eaf41b5002e17ffbd1fb7245999a073e8979085dacd47c4b4e8068ff5777142fc6726d6ca1fd2ff16921b48788b87225cbc57c72636f6efa8efbffe3
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"tsconfig-paths@npm:^3.14.1":
|
"tsconfig-paths@npm:^3.14.1":
|
||||||
version: 3.14.1
|
version: 3.14.1
|
||||||
resolution: "tsconfig-paths@npm:3.14.1"
|
resolution: "tsconfig-paths@npm:3.14.1"
|
||||||
@@ -8304,6 +8335,17 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"vitest-fetch-mock@npm:^0.2.2":
|
||||||
|
version: 0.2.2
|
||||||
|
resolution: "vitest-fetch-mock@npm:0.2.2"
|
||||||
|
dependencies:
|
||||||
|
cross-fetch: ^3.0.6
|
||||||
|
peerDependencies:
|
||||||
|
vitest: ">=0.16.0"
|
||||||
|
checksum: fa160f301171cd45dbf7d782880b6b6063fc74b9dd1965ef9206545e812ca8696e6be76662afbac822c6bf850fbb66cf8fb066af646e0e159f5a87ab25c97a02
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"vitest@npm:^0.29.3":
|
"vitest@npm:^0.29.3":
|
||||||
version: 0.29.3
|
version: 0.29.3
|
||||||
resolution: "vitest@npm:0.29.3"
|
resolution: "vitest@npm:0.29.3"
|
||||||
@@ -8378,6 +8420,13 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"webidl-conversions@npm:^3.0.0":
|
||||||
|
version: 3.0.1
|
||||||
|
resolution: "webidl-conversions@npm:3.0.1"
|
||||||
|
checksum: c92a0a6ab95314bde9c32e1d0a6dfac83b578f8fa5f21e675bc2706ed6981bc26b7eb7e6a1fab158e5ce4adf9caa4a0aee49a52505d4d13c7be545f15021b17c
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"webidl-conversions@npm:^7.0.0":
|
"webidl-conversions@npm:^7.0.0":
|
||||||
version: 7.0.0
|
version: 7.0.0
|
||||||
resolution: "webidl-conversions@npm:7.0.0"
|
resolution: "webidl-conversions@npm:7.0.0"
|
||||||
@@ -8430,6 +8479,16 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"whatwg-url@npm:^5.0.0":
|
||||||
|
version: 5.0.0
|
||||||
|
resolution: "whatwg-url@npm:5.0.0"
|
||||||
|
dependencies:
|
||||||
|
tr46: ~0.0.3
|
||||||
|
webidl-conversions: ^3.0.0
|
||||||
|
checksum: b8daed4ad3356cc4899048a15b2c143a9aed0dfae1f611ebd55073310c7b910f522ad75d727346ad64203d7e6c79ef25eafd465f4d12775ca44b90fa82ed9e2c
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"which-boxed-primitive@npm:^1.0.2":
|
"which-boxed-primitive@npm:^1.0.2":
|
||||||
version: 1.0.2
|
version: 1.0.2
|
||||||
resolution: "which-boxed-primitive@npm:1.0.2"
|
resolution: "which-boxed-primitive@npm:1.0.2"
|
||||||
|
|||||||
Reference in New Issue
Block a user