diff --git a/.github/workflows/auto-close-stale-pr.yml b/.github/workflows/auto-close-stale-pr.yml index 6ae3fec5..5bcaa05b 100644 --- a/.github/workflows/auto-close-stale-pr.yml +++ b/.github/workflows/auto-close-stale-pr.yml @@ -18,4 +18,4 @@ jobs: days-before-pr-stale: 21 days-before-pr-close: 7 exempt-draft-pr: true - repo-token: ${{ secrets.GITHUB_TOKEN }} + repo-token: ${{ secrets.JF_BOT_TOKEN }} diff --git a/.github/workflows/automations.yml b/.github/workflows/automations.yml index d7a2f3df..2086d6d7 100644 --- a/.github/workflows/automations.yml +++ b/.github/workflows/automations.yml @@ -30,4 +30,5 @@ jobs: uses: eps1lon/actions-label-merge-conflict@releases/2.x with: dirtyLabel: "merge conflict" - repoToken: ${{ secrets.GITHUB_TOKEN }} + commentOnDirty: "This pull request has merge conflicts. Please resolve the conflicts so the PR can be reviewed. Thanks!" + repoToken: ${{ secrets.JF_BOT_TOKEN }} diff --git a/.github/workflows/build-dev.yml b/.github/workflows/build-dev.yml index fd5e902e..3c945d5e 100644 --- a/.github/workflows/build-dev.yml +++ b/.github/workflows/build-dev.yml @@ -12,7 +12,7 @@ jobs: dev: runs-on: ubuntu-latest steps: - - uses: actions/checkout@8f4b7f84864484a7bf31766abe9204da3cbe65b3 # v3 + - uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3 - uses: actions/setup-node@64ed1c7eab4cce3362f8c340dee64e5eaeef8f7c # v3 with: node-version: "lts/*" diff --git a/.github/workflows/build-prod.yml b/.github/workflows/build-prod.yml index bb3039d9..ded52f4e 100644 --- a/.github/workflows/build-prod.yml +++ b/.github/workflows/build-prod.yml @@ -13,7 +13,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout master (the latest release) - uses: actions/checkout@8f4b7f84864484a7bf31766abe9204da3cbe65b3 # v3 + uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3 with: ref: master - name: Install jq to parse json @@ -33,7 +33,7 @@ jobs: - name: Save old Makefile version run: awk 'BEGIN { FS=" = " } /^VERSION/ { print "oldMakeVersion="$2; }' Makefile >> $GITHUB_ENV - name: Checkout PR branch - uses: actions/checkout@8f4b7f84864484a7bf31766abe9204da3cbe65b3 # v3 + uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3 - name: Save new package.json version run: echo "newPackVersion=$(jq -r ".version" package.json)" >> $GITHUB_ENV - name: package.json version must be updated @@ -61,7 +61,7 @@ jobs: prod: runs-on: ubuntu-latest steps: - - uses: actions/checkout@8f4b7f84864484a7bf31766abe9204da3cbe65b3 # v3 + - uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3 - uses: actions/setup-node@64ed1c7eab4cce3362f8c340dee64e5eaeef8f7c # v3 with: node-version: "lts/*" diff --git a/.gitignore b/.gitignore index 3d5bcad8..4ce5573c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,18 +1,17 @@ *.svg -jellyfin-roku.zip -source/globals.brs .env - -###BrightScript specific +# BrightScript dist/apps out/ - +build/ roku_modules - -#NPM modules +source/globals.brs +jellyfin-roku.zip +# Rooibos +bsconfig-tdd.json +# NPM node_modules/ - -#Eclipse +# Eclipse .buildpath .project .settings \ No newline at end of file diff --git a/.vscode/brighterscript.code-snippets b/.vscode/brighterscript.code-snippets new file mode 100644 index 00000000..23b25e21 --- /dev/null +++ b/.vscode/brighterscript.code-snippets @@ -0,0 +1,418 @@ +{ + "rooibos beforeEach": { + "prefix": "beforeEach", + "body": [ + "@beforeEach", + "function ${2:namespace}_${3:itGroup}_beforeEach()", + "\t$0", + "end function" + ] + }, + "rooibos afterEach": { + "prefix": "afterEach", + "body": [ + "@afterEach", + "function ${2:namespace}_${3:itGroup}_afterEach()", + "\t$0", + "end function" + ] + }, + "rooibos setup": { + "prefix": "setup", + "body": [ + "@setup", + "function ${2:namespace}_setup()", + "\t$0", + "end function" + ] + }, + "rooibos tearDown": { + "prefix": "tearDown", + "body": [ + "@tearDown", + "function ${2:namespace}_tearDown()", + "\t$0", + "end function" + ] + }, + "rooibos ignore": { + "prefix": "ignore", + "body": [ + "@ignore ${1:reason}", + "$0" + ] + }, + "rooibos only": { + "prefix": "only", + "body": [ + "@only", + "$0" + ] + }, + "rooibos testSuite": { + "prefix": "suite", + "body": [ + "@suite(\"$1\")", + "$0" + ] + }, + "rooibos testcase": { + "prefix": "it", + "body": [ + "@it(\"$1\")", + "function _()", + "\t$0", + "end function" + ] + }, + "rooibos params": { + "prefix": "params", + "body": [ + "@params(${1:values})$0" + ] + }, + "rooibos it": { + "prefix": "describe", + "body": [ + "'+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++", + "@describe(\"${1:groupName}\")", + "'+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++", + "", + "$0" + ] + }, + "rooibos stub": { + "prefix": "stub", + "body": [ + "m.stub(${1:target}, \"${2:methodName}\", [${3:methodArgs}], ${4:result})", + "$0" + ] + }, + "rooibos mock": { + "prefix": "expect", + "body": [ + "${1:mockName} = m.mock(${2:target}, \"${3:methodName}\", ${4:expectedNumberOfcalls}, [${5:methodArgs}], ${6:result})", + "$0" + ] + }, + "rooibos expect": { + "prefix": "expect", + "body": [ + "m.expectOnce(${1:target}, \"${2:methodName}\", ${3:expectedNumberOfcalls}, [${4:methodArgs}], ${5:result})", + "$0" + ] + }, + "rooibos expectOnce": { + "prefix": "expectOnce", + "body": [ + "m.expectOnce(${1:target}, \"${2:methodName}\", [${3:methodArgs}], ${4:result})", + "$0" + ] + }, + "rooibos expectCallfunc": { + "prefix": "expectCallfunc", + "body": [ + "m.expectOnce(${1:target}, \"callFunc\", [\"${2:methodName}\", ${3:methodArgs}], ${4:result})", + "$0" + ] + }, + "rooibos expectObserveNodeField": { + "prefix": "eonf", + "body": [ + "m.expectOnce(${1:target}, \"observeNodeField\", [${2:node},\"${3:fieldName}\", m.${4:callback}])", + "$0" + ] + }, + "rooibos expectUnObserveNodeField": { + "prefix": "eunf", + "body": [ + "m.expectOnce(${1:target}, \"unobserveNodeField\", [${2:node},\"${:fieldName}\", m.${4:callback}])", + "$0" + ] + }, + "rooibos expectObjectOnce": { + "prefix": "expectObjectOnce", + "body": [ + "${1:name} = { \"id\" : \"${1:name}\" }", + "m.expectOnce(${2:target}, \"${3:methodName}\", [${4:methodArgs}], ${1:name})", + "$0" + ] + }, + "rooibos expectGetInstance": { + "prefix": "expectGetInstance", + "body": [ + "${1:name} = { \"id\" : \"${1:name}\" }", + "m.expectOnce(${2:target}, \"getInstance\", [\"${3:instanceName}\"], ${1:name})", + "$0" + ] + }, + "rooibos expectCreateSGNode": { + "prefix": "expectCreateSGNode", + "body": [ + "${1:name} = { \"id\" : \"${1:name}\" }", + "m.expectOnce(${2:target}, \"createSGNode\", [\"${3:nodeType}\"$0], ${1:name})" + ] + }, + "rooibos expectGetClassInstance": { + "prefix": "expectGetClassInstance", + "body": [ + "${1:name} = { \"id\" : \"${1:name}\" }", + "m.expectOnce(${2:target}, \"getClassInstance\", [\"${3:instanceName}\"], ${1:name})", + "$0" + ] + }, + "rooibos expectExpectOnce": { + "prefix": "expectExpect", + "body": [ + "${1:name} = { \"id\" : \"${1:name}\" }", + "m.expectOnce(${2:target}, \"${3:methodName}\", [${4:methodArgs}], ${1:name})", + "m.expectOnce(${1:name}, \"${5:methodName}\", [${6:methodArgs}], ${7:name})", + "$0" + ] + }, + "rooibos expectNone": { + "prefix": "expectNone", + "body": [ + "m.expectNone(${1:target}, \"${2:methodName}\")", + "$0" + ] + }, + "rooibos assertFalse": { + "prefix": "assertFalse", + "body": [ + "m.assertFalse(${1:value})", + "$0" + ] + }, + "rooibos assertAsync": { + "prefix": "assertAsync", + "body": [ + "m.AssertAsyncField(${1:value}, $2{:fieldName})", + "$0" + ] + }, + "rooibos assertTrue": { + "prefix": "assertTrue", + "body": [ + "m.assertTrue(${1:value})", + "$0" + ] + }, + "rooibos assertEqual": { + "prefix": "assertEqual", + "body": [ + "m.assertEqual(${1:value}, ${2:expected})", + "$0" + ] + }, + "rooibos assertLike": { + "prefix": "assertLike", + "body": [ + "m.assertLike(${1:value}, ${2:expected})", + "$0" + ] + }, + "rooibos assertNotEqual": { + "prefix": "assertNotEqual", + "body": [ + "m.assertNotEqual(${1:value}, ${2:expected})", + "$0" + ] + }, + "rooibos assertInvalid": { + "prefix": "assertInvalid", + "body": [ + "m.assertInvalid(${1:value})", + "$0" + ] + }, + "rooibos assertNotInvalid": { + "prefix": "assertNotInvalid", + "body": [ + "m.assertNotInvalid(${1:value})", + "$0" + ] + }, + "rooibos assertAAHasKey": { + "prefix": "assertAAHasKey", + "body": [ + "m.assertAAHasKey(${1:value}, ${2:expected})", + "$0" + ] + }, + "rooibos assertAANotHasKey": { + "prefix": "assertAANotHasKey", + "body": [ + "m.assertAANotHasKey(${1:value}, ${2:expected})", + "$0" + ] + }, + "rooibos assertAAHasKeys": { + "prefix": "assertAAHasKeys", + "body": [ + "m.assertAAHasKeys(${1:value}, ${2:expected})", + "$0" + ] + }, + "rooibos assertAANotHasKeys": { + "prefix": "assertAANotHasKeys", + "body": [ + "m.assertAANotHasKeys(${1:value}, ${2:expected})", + "$0" + ] + }, + "rooibos assertArrayContains": { + "prefix": "assertArrayContains", + "body": [ + "m.assertArrayContains(${1:value}, ${2:expected})", + "$0" + ] + }, + "rooibos assertArrayNotContains": { + "prefix": "assertArrayNotContains", + "body": [ + "m.assertArrayNotContains(${1:value}, ${2:expected})", + "$0" + ] + }, + "rooibos assertArrayContainsSubset": { + "prefix": "assertArrayContainsSubset", + "body": [ + "m.assertArrayContainsSubset(${1:value}, ${2:expected})", + "$0" + ] + }, + "rooibos assertArrayContainsAAs": { + "prefix": "assertArrayContainsAAs", + "body": [ + "m.assertArrayContainsAAs(${1:value}, ${2:expected})", + "$0" + ] + }, + "rooibos assertArrayNotContainsSubset": { + "prefix": "assertArrayNotContainsSubset", + "body": [ + "m.assertArrayNotContainsSubset(${1:value}, ${2:expected})", + "$0" + ] + }, + "rooibos assertArrayCount": { + "prefix": "assertArrayCount", + "body": [ + "m.assertArrayCount(${1:value}, ${2:expected})", + "$0" + ] + }, + "rooibos assertArrayNotCount": { + "prefix": "assertArrayNotCount", + "body": [ + "m.assertArrayNotCount(${1:value}, ${2:expected})", + "$0" + ] + }, + "rooibos assertEmpty": { + "prefix": "assertEmpty", + "body": [ + "m.assertEmpty(${1:value})", + "$0" + ] + }, + "rooibos assertNotEmpty": { + "prefix": "assertNotEmpty", + "body": [ + "m.assertNotEmpty(${1:value})", + "$0" + ] + }, + "rooibos assertArrayContainsOnlyValuesOfType": { + "prefix": "assertArrayContainsOnlyValuesOfType", + "body": [ + "m.assertArrayContainsOnlyValuesOfType(${1:value}, ${2:expected})", + "$0" + ] + }, + "rooibos assertType": { + "prefix": "assertType", + "body": [ + "m.assertType(${1:value}, ${2:expected})", + "$0" + ] + }, + "rooibos assertSubType": { + "prefix": "assertSubType", + "body": [ + "m.assertSubType(${1:value}, ${2:expected})", + "$0" + ] + }, + "rooibos assertNodeCount": { + "prefix": "assertNodeCount", + "body": [ + "m.assertNodeCount(${1:value}, ${2:expected})", + "$0" + ] + }, + "rooibos assertNodeNotCount": { + "prefix": "assertNodeNotCount", + "body": [ + "m.assertNodeNotCount(${1:value}, ${2:expected})", + "$0" + ] + }, + "rooibos assertNodeEmpty": { + "prefix": "assertNodeEmpty", + "body": [ + "m.assertNodeEmpty(${1:value})", + "$0" + ] + }, + "rooibos assertNodeNotEmpty": { + "prefix": "assertNodeNotEmpty", + "body": [ + "m.assertNodeNotEmpty(${1:value})", + "$0" + ] + }, + "rooibos assertNodeContains": { + "prefix": "assertNodeContains", + "body": [ + "m.assertNodeContains(${1:value}, ${2:expected})", + "$0" + ] + }, + "rooibos assertNodeNotContains": { + "prefix": "assertNodeNotContains", + "body": [ + "m.assertNodeNotContains(${1:value}, ${2:expected})", + "$0" + ] + }, + "rooibos assertNodeContainsFields": { + "prefix": "assertNodeContainsFields", + "body": [ + "m.assertNodeContainsFields(${1:value}, ${2:expected})", + "$0" + ] + }, + "rooibos assertNodeNotContainsFields": { + "prefix": "assertNodeNotContainsFields", + "body": [ + "m.assertNodeNotContainsFields(${1:value}, ${2:expected})", + "$0" + ] + }, + "rooibos assertAAContainsSubset": { + "prefix": "assertAAContainsSubset", + "body": [ + "m.assertAAContainsSubset(${1:value}, ${2:expected})", + "$0" + ] + }, + "rooibos assertMocks": { + "prefix": "assertMocks", + "body": [ + "m.assertMocks(${1:value}, ${2:expected})", + "$0" + ] + } +} \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json index 4a9b3200..c4673d71 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -4,7 +4,7 @@ { "type": "brightscript", "request": "launch", - "name": "Jellyfin Debug: Launch", + "name": "Jellyfin Debug", "stopOnEntry": false, // To enable RALE: // set "brightscript.debug.raleTrackerTaskFileLocation": "/absolute/path/to/rale/TrackerTask.xml" in your vscode user settings @@ -22,6 +22,66 @@ "source/**/*", "manifest" ] + }, + { + "name": "Run tests", + "type": "brightscript", + "request": "launch", + "consoleOutput": "full", + "internalConsoleOptions": "neverOpen", + "preLaunchTask": "build-tests", + "retainStagingFolder": true, + "stopOnEntry": false, + "files": [ + "!**/images/*.*", + "!**/fonts/*.*", + "!*.jpg", + "!*.png", + "*", + "*.*", + "**/*.*", + "!*.zip", + "!**/*.zip" + ], + "rootDir": "${workspaceFolder}/build", + "sourceDirs": [ + "${workspaceFolder}/test-app" + ], + "enableDebuggerAutoRecovery": true, + "stopDebuggerOnAppExit": true, + "enableVariablesPanel": false, + "injectRaleTrackerTask": false, + "enableDebugProtocol": false + }, + { + "name": "Run test-tdd", + "type": "brightscript", + "request": "launch", + "consoleOutput": "full", + "internalConsoleOptions": "neverOpen", + "preLaunchTask": "build-tdd", + "retainStagingFolder": true, + "stopOnEntry": false, + "files": [ + "!**/images/*.*", + "!**/fonts/*.*", + "!*.jpg", + "!*.png", + "*", + "*.*", + "**/*.*", + "!*.zip", + "!**/*.zip" + ], + "rootDir": "${workspaceFolder}/build", + "sourceDirs": [ + "${workspaceFolder}/test-app" + ], + "enableDebuggerAutoRecovery": true, + "stopDebuggerOnAppExit": true, + "enableVariablesPanel": false, + "injectRaleTrackerTask": false, + "enableDebugProtocol": false } ] } \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json index 65c7ba47..a2379557 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -2,6 +2,13 @@ "files.associations": { "*.ts": "xml" }, + "[xml]": { + "editor.defaultFormatter": "redhat.vscode-xml" + }, + "[markdown]": { + "editor.defaultFormatter": "DavidAnson.vscode-markdownlint" + }, "xml.format.maxLineWidth": 0, - "editor.formatOnSave": true + "editor.formatOnSave": true, + "brightscript.bsdk": "node_modules/brighterscript" } \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 00000000..232baf36 --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,41 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "build-tests", + "type": "shell", + "command": "npm run build-tests", + "problemMatcher": [], + "presentation": { + "echo": true, + "reveal": "silent", + "focus": false, + "panel": "shared", + "showReuseMessage": false, + "clear": true + }, + "group": { + "kind": "build", + "isDefault": true + } + }, + { + "label": "build-tdd", + "type": "shell", + "command": "npm run build-tdd", + "problemMatcher": [], + "presentation": { + "echo": true, + "reveal": "silent", + "focus": false, + "panel": "shared", + "showReuseMessage": false, + "clear": true + }, + "group": { + "kind": "build", + "isDefault": true + } + } + ] +} \ No newline at end of file diff --git a/bsconfig-tests.json b/bsconfig-tests.json new file mode 100644 index 00000000..ee5a654b --- /dev/null +++ b/bsconfig-tests.json @@ -0,0 +1,44 @@ +{ + "files": [ + { + "src": "test-app/**/*" + }, + { + "src": "source/**/!(Main.brs)", + "dest": "source" + }, + { + "src": "components/**/*", + "dest": "components" + }, + { + "src": "locale/**/*", + "dest": "locale" + }, + { + "src": "settings/**/*", + "dest": "settings" + } + ], + "autoImportComponentScript": true, + "createPackage": false, + "stagingFolderPath": "build", + "plugins": [ + "rooibos-roku" + ], + "rooibos": { + "isRecordingCodeCoverage": false, + "testsFilePattern": null, + "tags": [ + "!integration", + "!deprecated", + "!fixme" + ], + "showOnlyFailures": true, + "catchCrashes": true, + "lineWidth": 70, + "failFast": false, + "sendHomeOnFinish": false + }, + "sourceMap": true +} \ No newline at end of file diff --git a/bsconfig.json b/bsconfig.json index 4045d2b8..61a5eb62 100644 --- a/bsconfig.json +++ b/bsconfig.json @@ -9,11 +9,11 @@ "settings/*.*" ], "plugins": [ - "@rokucommunity/bslint" + "@rokucommunity/bslint", + "rooibos-roku" ], "diagnosticFilters": [ - "**/roku_modules/**/*", - "**/testFramework/*", - "**/tests/*" + "node_modules/**", + "**/roku_modules/**" ] } \ No newline at end of file diff --git a/bsfmt.json b/bsfmt.json index ce0f85d4..3deaa0f9 100644 --- a/bsfmt.json +++ b/bsfmt.json @@ -1,6 +1,9 @@ { "files": [ "source/**/*.brs", - "components/**/*.brs" + "source/**/*.bs", + "components/**/*.brs", + "components/**/*.bs", + "test-app/**/*.bs" ] -} +} \ No newline at end of file diff --git a/components/ItemGrid/LoadVideoContentTask.brs b/components/ItemGrid/LoadVideoContentTask.brs index b82d7a3c..d6641899 100644 --- a/components/ItemGrid/LoadVideoContentTask.brs +++ b/components/ItemGrid/LoadVideoContentTask.brs @@ -13,7 +13,7 @@ sub loadItems() m.top.content = [LoadItems_VideoPlayer(m.top.itemId)] end sub -function LoadItems_VideoPlayer(id, mediaSourceId = invalid, audio_stream_idx = 1, subtitle_idx = -1, forceTranscoding = false, showIntro = true, allowResumeDialog = true) +function LoadItems_VideoPlayer(id as string, mediaSourceId = invalid as dynamic, audio_stream_idx = 1 as integer, subtitle_idx = -1 as integer, forceTranscoding = false as boolean, showIntro = true as boolean, allowResumeDialog = true as boolean) as dynamic video = {} video.id = id @@ -32,7 +32,7 @@ function LoadItems_VideoPlayer(id, mediaSourceId = invalid, audio_stream_idx = 1 return video end function -sub LoadItems_AddVideoContent(video, mediaSourceId, audio_stream_idx = 1, subtitle_idx = -1, playbackPosition = -1, forceTranscoding = false, showIntro = true, allowResumeDialog = true) +sub LoadItems_AddVideoContent(video as object, mediaSourceId as dynamic, audio_stream_idx = 1 as integer, subtitle_idx = -1 as integer, playbackPosition = -1 as integer, forceTranscoding = false as boolean, showIntro = true as boolean, allowResumeDialog = true as boolean) meta = ItemMetaData(video.id) diff --git a/components/PlayedCheckmark.brs b/components/PlayedCheckmark.brs new file mode 100644 index 00000000..205bb6c2 --- /dev/null +++ b/components/PlayedCheckmark.brs @@ -0,0 +1,4 @@ +sub init() + checkmark = m.top.findNode("checkmark") + checkmark.font.size = 48 +end sub diff --git a/components/PlayedCheckmark.xml b/components/PlayedCheckmark.xml new file mode 100644 index 00000000..9d6285c6 --- /dev/null +++ b/components/PlayedCheckmark.xml @@ -0,0 +1,7 @@ + + + + +