update lock

snyk-upgrade-360291ec0e19ef23e898d7fa71c7050b
tidusjar 1 year ago
commit a6b11b33cd

@ -43,8 +43,8 @@ jobs:
- name: Run Docker Image - name: Run Docker Image
run: nohup docker run --rm -p 5000:5000 ombi & run: nohup docker run --rm -p 5000:5000 ombi &
# - name: Run Wiremock Plex - name: Run Wiremock
# run: nohup docker run -it --rm -p 32400:8080 --name wiremock wiremock/wiremock:2.35.0 run: nohup docker run --rm -p 32400:8080 --name wiremock wiremock/wiremock:2.35.0 &
- name: Sleep for server to start - name: Sleep for server to start
run: sleep 20 run: sleep 20
@ -61,7 +61,7 @@ jobs:
# nohup dotnet run --project ./src/Ombi -- --host http://*:3577 & # nohup dotnet run --project ./src/Ombi -- --host http://*:3577 &
- name: Cypress Tests - name: Cypress Tests
uses: cypress-io/github-action@v2.8.2 uses: cypress-io/github-action@v4
with: with:
record: true record: true
browser: chrome browser: chrome
@ -73,3 +73,9 @@ jobs:
env: env:
CYPRESS_RECORD_KEY: ${{ secrets.CYPRESS_RECORD_KEY }} CYPRESS_RECORD_KEY: ${{ secrets.CYPRESS_RECORD_KEY }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Stop Docker
if: always()
run: |
docker ps -q | xargs -I {} docker logs {}
docker container kill $(docker ps -q)

@ -1,47 +1,47 @@
name: 'Chromatic' # name: 'Chromatic'
# Event for the workflow # # Event for the workflow
on: # on:
push: # push:
workflow_dispatch: # workflow_dispatch:
# List of jobs # # List of jobs
jobs: # jobs:
storybook-build: # storybook-build:
# Operating System # # Operating System
runs-on: ubuntu-latest # runs-on: ubuntu-latest
steps: # steps:
- name: Checkout repository # - name: Checkout repository
uses: actions/checkout@v2 # uses: actions/checkout@v2
with: # with:
fetch-depth: 0 # fetch-depth: 0
- name: NodeModules Cache # - name: NodeModules Cache
uses: actions/cache@v2 # uses: actions/cache@v2
with: # with:
path: '**/node_modules' # path: '**/node_modules'
key: node_modules-${{ hashFiles('**/yarn.lock') }} # key: node_modules-${{ hashFiles('**/yarn.lock') }}
- name: Install dependencies # - name: Install dependencies
working-directory: ./src/Ombi/ClientApp # working-directory: ./src/Ombi/ClientApp
run: yarn # run: yarn
- name: Publish to Chromatic # - name: Publish to Chromatic
if: github.ref != 'refs/heads/master' # if: github.ref != 'refs/heads/master'
uses: chromaui/action@v1 # uses: chromaui/action@v1
with: # with:
projectToken: 7c47e1a1a4bd # projectToken: 7c47e1a1a4bd
exitZeroOnChanges: true # exitZeroOnChanges: true
workingDir: ./src/Ombi/ClientApp # workingDir: ./src/Ombi/ClientApp
buildScriptName: storybookbuild # buildScriptName: storybookbuild
exitOnceUploaded: true # exitOnceUploaded: true
- name: Publish to Chromatic and auto accept changes # - name: Publish to Chromatic and auto accept changes
if: github.ref == 'refs/heads/develop' # if: github.ref == 'refs/heads/develop'
uses: chromaui/action@v1 # uses: chromaui/action@v1
with: # with:
projectToken: 7c47e1a1a4bd # projectToken: 7c47e1a1a4bd
autoAcceptChanges: true # 👈 Option to accept all changes # autoAcceptChanges: true # 👈 Option to accept all changes
workingDir: ./src/Ombi/ClientApp # workingDir: ./src/Ombi/ClientApp
buildScriptName: storybookbuild # buildScriptName: storybookbuild
exitOnceUploaded: true # exitOnceUploaded: true

@ -1,3 +1,18 @@
## [4.42.1](https://github.com/Ombi-app/Ombi/compare/v4.42.0...v4.42.1) (2023-06-20)
### Bug Fixes
* More automation tests mainly around the Plex Settings page ([#4821](https://github.com/Ombi-app/Ombi/issues/4821)) ([21bfc5a](https://github.com/Ombi-app/Ombi/commit/21bfc5a45adf6da6a80854e19494a8ffdc9c0761))
* src/Ombi.Notifications/Ombi.Notifications.csproj to reduce vulnerabilities ([#4969](https://github.com/Ombi-app/Ombi/issues/4969)) [skip ci] ([8584ad4](https://github.com/Ombi-app/Ombi/commit/8584ad46053c51f5da40b24f3efd1b9e5a031ddd))
* upgrade @fortawesome/fontawesome-free from 6.1.2 to 6.4.0 ([#4965](https://github.com/Ombi-app/Ombi/issues/4965)) [skip ci] ([84454e5](https://github.com/Ombi-app/Ombi/commit/84454e53c00c808e8a393c7750bdc418a7593e91))
* upgrade @microsoft/signalr from 6.0.11 to 6.0.16 ([#4964](https://github.com/Ombi-app/Ombi/issues/4964)) [skip ci] ([a0201e3](https://github.com/Ombi-app/Ombi/commit/a0201e3f585dc52f717e33c46ede35a4eccac736))
* upgrade cypress-real-events from 1.7.4 to 1.8.1 ([#4968](https://github.com/Ombi-app/Ombi/issues/4968)) [skip ci] ([8a24b56](https://github.com/Ombi-app/Ombi/commit/8a24b56299c3bc98bf0d719ba448972aaa7f7461))
* upgrade multiple dependencies with Snyk ([#4963](https://github.com/Ombi-app/Ombi/issues/4963)) [skip ci] ([6025c5e](https://github.com/Ombi-app/Ombi/commit/6025c5ed757438d3a5d79bd36fd789ef0297ce70))
* upgrade primeng from 15.0.0-rc.1 to 15.4.1 ([#4962](https://github.com/Ombi-app/Ombi/issues/4962)) [skip ci] ([23a4fed](https://github.com/Ombi-app/Ombi/commit/23a4fede69898a25b342aed78a8cda553c1fd18d))
# [4.42.0](https://github.com/Ombi-app/Ombi/compare/v4.41.1...v4.42.0) (2023-06-02) # [4.42.0](https://github.com/Ombi-app/Ombi/compare/v4.41.1...v4.42.0) (2023-06-02)
@ -2432,12 +2447,3 @@
## [4.32.3](https://github.com/Ombi-app/Ombi/compare/v4.32.2...v4.32.3) (2022-11-24)
### Bug Fixes
* **sonarr:** V4 actually works this time around ([f62e70f](https://github.com/Ombi-app/Ombi/commit/f62e70fc493c7971da5e4508ce10522f5df0bbf7))

@ -6,7 +6,7 @@ ____
[![Github All Releases](https://img.shields.io/github/downloads/tidusjar/Ombi/total.svg)](https://github.com/ombi-app/Ombi) [![Github All Releases](https://img.shields.io/github/downloads/tidusjar/Ombi/total.svg)](https://github.com/ombi-app/Ombi)
[![firsttimersonly](http://img.shields.io/badge/first--timers--only-friendly-blue.svg)](http://www.firsttimersonly.com/) [![firsttimersonly](http://img.shields.io/badge/first--timers--only-friendly-blue.svg)](http://www.firsttimersonly.com/)
[![Crowdin](https://d322cqt584bo4o.cloudfront.net/ombi/localized.svg)](https://crowdin.com/project/ombi) [![Crowdin](https://d322cqt584bo4o.cloudfront.net/ombi/localized.svg)](https://crowdin.com/project/ombi)
[![Automation Tests](https://github.com/Ombi-app/Ombi/actions/workflows/cypress.yml/badge.svg)](https://github.com/Ombi-app/Ombi/actions/workflows/cypress.yml) [![Automation Tests](https://github.com/Ombi-app/Ombi/actions/workflows/automation-tests.yml/badge.svg)](https://github.com/Ombi-app/Ombi/actions/workflows/automation-tests.yml)
[![Patreon](https://img.shields.io/badge/patreon-donate-yellow.svg)](https://patreon.com/tidusjar/Ombi) [![Patreon](https://img.shields.io/badge/patreon-donate-yellow.svg)](https://patreon.com/tidusjar/Ombi)
[![Paypal](https://img.shields.io/badge/paypal-donate-yellow.svg)](https://paypal.me/PlexRequestsNet) [![Paypal](https://img.shields.io/badge/paypal-donate-yellow.svg)](https://paypal.me/PlexRequestsNet)

@ -18,7 +18,7 @@
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference> </PackageReference>
<packagereference Include="Microsoft.NET.Test.Sdk" Version="17.3.2"></packagereference> <packagereference Include="Microsoft.NET.Test.Sdk" Version="17.6.2"></packagereference>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

@ -12,7 +12,7 @@
<PackageReference Include="nunit" Version="3.13.3" /> <PackageReference Include="nunit" Version="3.13.3" />
<PackageReference Include="Microsoft.Bcl.AsyncInterfaces" Version="6.0.0" /> <PackageReference Include="Microsoft.Bcl.AsyncInterfaces" Version="6.0.0" />
<PackageReference Include="NUnit3TestAdapter" Version="4.2.1" /> <PackageReference Include="NUnit3TestAdapter" Version="4.2.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.3.2" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.6.2" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

@ -17,7 +17,7 @@
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="6.0.2" /> <PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="6.0.2" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" /> <PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
<PackageReference Include="Nito.AsyncEx" Version="5.1.2" /> <PackageReference Include="Nito.AsyncEx" Version="5.1.2" />
<PackageReference Include="Quartz" Version="3.5.0" /> <PackageReference Include="Quartz" Version="3.6.2" />
<PackageReference Include="System.Security.Claims" Version="4.3.0" /> <PackageReference Include="System.Security.Claims" Version="4.3.0" />
</ItemGroup> </ItemGroup>

@ -11,7 +11,7 @@
<PackageReference Include="NUnit.ConsoleRunner" Version="3.15.2" /> <PackageReference Include="NUnit.ConsoleRunner" Version="3.15.2" />
<PackageReference Include="NUnit3TestAdapter" Version="4.2.1" /> <PackageReference Include="NUnit3TestAdapter" Version="4.2.1" />
<PackageReference Include="Microsoft.Bcl.AsyncInterfaces" Version="6.0.0" /> <PackageReference Include="Microsoft.Bcl.AsyncInterfaces" Version="6.0.0" />
<packagereference Include="Microsoft.NET.Test.Sdk" Version="17.3.2"></packagereference> <packagereference Include="Microsoft.NET.Test.Sdk" Version="17.6.2"></packagereference>
<PackageReference Include="Moq" Version="4.18.2" /> <PackageReference Include="Moq" Version="4.18.2" />
<PackageReference Include="Moq.AutoMock" Version="3.4.0" /> <PackageReference Include="Moq.AutoMock" Version="3.4.0" />
</ItemGroup> </ItemGroup>

@ -13,7 +13,7 @@
<PackageReference Include="Microsoft.Bcl.AsyncInterfaces" Version="6.0.0" /> <PackageReference Include="Microsoft.Bcl.AsyncInterfaces" Version="6.0.0" />
<PackageReference Include="NUnit.ConsoleRunner" Version="3.15.2" /> <PackageReference Include="NUnit.ConsoleRunner" Version="3.15.2" />
<PackageReference Include="NUnit3TestAdapter" Version="4.2.1" /> <PackageReference Include="NUnit3TestAdapter" Version="4.2.1" />
<packagereference Include="Microsoft.NET.Test.Sdk" Version="17.3.2"></packagereference> <packagereference Include="Microsoft.NET.Test.Sdk" Version="17.6.2"></packagereference>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

@ -11,7 +11,7 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Quartz" Version="3.5.0" /> <PackageReference Include="Quartz" Version="3.6.2" />
<PackageReference Include="Serilog" Version="2.12.0" /> <PackageReference Include="Serilog" Version="2.12.0" />
<PackageReference Include="SharpCompress" Version="0.32.2" /> <PackageReference Include="SharpCompress" Version="0.32.2" />
<PackageReference Include="System.Diagnostics.Process" Version="4.3.0" /> <PackageReference Include="System.Diagnostics.Process" Version="4.3.0" />

@ -11,7 +11,7 @@
<PackageReference Include="nunit" Version="3.13.3" /> <PackageReference Include="nunit" Version="3.13.3" />
<PackageReference Include="NUnit3TestAdapter" Version="4.2.1" /> <PackageReference Include="NUnit3TestAdapter" Version="4.2.1" />
<PackageReference Include="Microsoft.Bcl.AsyncInterfaces" Version="6.0.0" /> <PackageReference Include="Microsoft.Bcl.AsyncInterfaces" Version="6.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.3.2" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.6.2" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

@ -12,7 +12,7 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="6.0.0" /> <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="6.0.0" />
<PackageReference Include="Quartz" Version="3.5.0" /> <PackageReference Include="Quartz" Version="3.6.2" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" /> <PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
</ItemGroup> </ItemGroup>

@ -7,7 +7,6 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Identity" Version="2.2.0" />
<PackageReference Include="Moq" Version="4.18.2" /> <PackageReference Include="Moq" Version="4.18.2" />
<PackageReference Include="MockQueryable.Moq" Version="6.0.1" /> <PackageReference Include="MockQueryable.Moq" Version="6.0.1" />
</ItemGroup> </ItemGroup>

@ -14,7 +14,7 @@
<PackageReference Include="NUnit.ConsoleRunner" Version="3.15.2" /> <PackageReference Include="NUnit.ConsoleRunner" Version="3.15.2" />
<PackageReference Include="Microsoft.Bcl.AsyncInterfaces" Version="6.0.0" /> <PackageReference Include="Microsoft.Bcl.AsyncInterfaces" Version="6.0.0" />
<PackageReference Include="NUnit3TestAdapter" Version="4.2.1" /> <PackageReference Include="NUnit3TestAdapter" Version="4.2.1" />
<packagereference Include="Microsoft.NET.Test.Sdk" Version="17.3.2"></packagereference> <packagereference Include="Microsoft.NET.Test.Sdk" Version="17.6.2"></packagereference>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

@ -37,7 +37,7 @@
"angularx-qrcode": "^15.0.0", "angularx-qrcode": "^15.0.0",
"bootstrap": "^4.2.1", "bootstrap": "^4.2.1",
"core-js": "^2.5.4", "core-js": "^2.5.4",
"jquery": "3.6.1", "jquery": "3.7.0",
"lodash": "^4.17.21", "lodash": "^4.17.21",
"moment": "^2.29.1", "moment": "^2.29.1",
"ng2-cookies": "^1.0.12", "ng2-cookies": "^1.0.12",
@ -50,8 +50,7 @@
"primeng": "^15.4.1", "primeng": "^15.4.1",
"rxjs": "^7.5.4", "rxjs": "^7.5.4",
"ts-md5": "^1.2.7", "ts-md5": "^1.2.7",
"tslint-angular": "^1.1.2", "zone.js": "~0.13.0"
"zone.js": "~0.11.4"
}, },
"devDependencies": { "devDependencies": {
"@angular-devkit/build-angular": "^15.0.2", "@angular-devkit/build-angular": "^15.0.2",

@ -5,47 +5,47 @@
<mat-form-field appearance="outline" floatLabel=auto> <mat-form-field appearance="outline" floatLabel=auto>
<mat-label>Server Name</mat-label> <mat-label>Server Name</mat-label>
<input matInput placeholder="Server Name" name="name" [(ngModel)]="this.data.server.name" value="{{this.data.server.name}}"> <input matInput id="serverName" placeholder="Server Name" name="name" [(ngModel)]="this.data.server.name" value="{{this.data.server.name}}">
</mat-form-field> </mat-form-field>
<div class="row"> <div class="row">
<mat-form-field class="col-md-6 col-12" appearance="outline" floatLabel=auto> <mat-form-field class="col-md-6 col-12" appearance="outline" floatLabel=auto>
<mat-label>Hostname / IP</mat-label> <mat-label>Hostname / IP</mat-label>
<input matInput placeholder="Hostname or IP" name="ip" [(ngModel)]="this.data.server.ip" value="{{this.data.server.ip}}" <input matInput id="ip" placeholder="Hostname or IP" name="ip" [(ngModel)]="this.data.server.ip" value="{{this.data.server.ip}}"
#serverHostnameIpControl="ngModel" required> #serverHostnameIpControl="ngModel" required>
<mat-error *ngIf="serverHostnameIpControl.hasError('required')">Must be specified.</mat-error> <mat-error *ngIf="serverHostnameIpControl.hasError('required')">Must be specified.</mat-error>
</mat-form-field> </mat-form-field>
<mat-form-field class="col-md-4 col-7" appearance="outline" floatLabel=auto> <mat-form-field class="col-md-4 col-7" appearance="outline" floatLabel=auto>
<mat-label>Port</mat-label> <mat-label>Port</mat-label>
<input matInput placeholder="Port" name="port" [(ngModel)]="this.data.server.port" value="{{this.data.server.port}}" <input id="port" matInput placeholder="Port" name="port" [(ngModel)]="this.data.server.port" value="{{this.data.server.port}}"
#serverPortControl="ngModel" required pattern="^[0-9]*$"> #serverPortControl="ngModel" required pattern="^[0-9]*$">
<mat-error *ngIf="serverPortControl.hasError('required')">Must be specified.</mat-error> <mat-error *ngIf="serverPortControl.hasError('required')">Must be specified.</mat-error>
<mat-error *ngIf="serverPortControl.hasError('pattern')">Must be a number.</mat-error> <mat-error *ngIf="serverPortControl.hasError('pattern')">Must be a number.</mat-error>
</mat-form-field> </mat-form-field>
<mat-slide-toggle class="col-md-2 col-5 mt-3" id="ssl" name="ssl" [(ngModel)]="this.data.server.ssl" [checked]="this.data.server.ssl"> <mat-slide-toggle id="ssl" class="col-md-2 col-5 mt-3" id="ssl" name="ssl" [(ngModel)]="this.data.server.ssl" [checked]="this.data.server.ssl">
SSL SSL
</mat-slide-toggle> </mat-slide-toggle>
</div> </div>
<mat-form-field appearance="outline" floatLabel=auto> <mat-form-field appearance="outline" floatLabel=auto>
<mat-label>Plex Authorization Token</mat-label> <mat-label>Plex Authorization Token</mat-label>
<input matInput placeholder="Plex Authorization Token" name="authToken" [(ngModel)]="this.data.server.plexAuthToken" value="{{this.data.server.plexAuthToken}}" <input id="authToken" matInput placeholder="Plex Authorization Token" name="authToken" [(ngModel)]="this.data.server.plexAuthToken" value="{{this.data.server.plexAuthToken}}"
#serverApiKeyControl="ngModel" required> #serverApiKeyControl="ngModel" required>
<mat-error *ngIf="serverApiKeyControl.hasError('required')">Must be specified.</mat-error> <mat-error *ngIf="serverApiKeyControl.hasError('required')">Must be specified.</mat-error>
</mat-form-field> </mat-form-field>
<mat-form-field appearance="outline" floatLabel=auto> <mat-form-field appearance="outline" floatLabel=auto>
<mat-label>Machine Identifier</mat-label> <mat-label>Machine Identifier</mat-label>
<input matInput placeholder="Machine Identifier" name="MachineIdentifier" [(ngModel)]="this.data.server.machineIdentifier" value="{{this.data.server.machineIdentifier}}" <input id="machineId" matInput placeholder="Machine Identifier" name="MachineIdentifier" [(ngModel)]="this.data.server.machineIdentifier" value="{{this.data.server.machineIdentifier}}"
#serverApiKeyControl="ngModel" required> #serverApiKeyControl="ngModel" required>
<mat-error *ngIf="serverApiKeyControl.hasError('required')">Must be specified.</mat-error> <mat-error *ngIf="serverApiKeyControl.hasError('required')">Must be specified.</mat-error>
</mat-form-field> </mat-form-field>
<mat-form-field appearance="outline" floatLabel=auto> <mat-form-field appearance="outline" floatLabel=auto>
<mat-label>Externally Facing Hostname</mat-label> <mat-label>Externally Facing Hostname</mat-label>
<input matInput placeholder="e.g. https://emby.this.data.server.com/" name="serverHostname" name="hostname" <input id="externalHostname" matInput placeholder="e.g. https://emby.this.data.server.com/" name="serverHostname" name="hostname"
[(ngModel)]="this.data.server.serverHostname" value="{{this.data.server.serverHostname}}" > [(ngModel)]="this.data.server.serverHostname" value="{{this.data.server.serverHostname}}" >
<mat-hint> <mat-hint>
This will be the external address that users will navigate to when they press the 'View On Plex' button This will be the external address that users will navigate to when they press the 'View On Plex' button
@ -58,7 +58,7 @@
<mat-form-field appearance="outline" floatLabel=auto> <mat-form-field appearance="outline" floatLabel=auto>
<mat-label>Episode Batch Size</mat-label> <mat-label>Episode Batch Size</mat-label>
<input matInput placeholder="150" name="MachineIdentifier" [(ngModel)]="this.data.server.episodeBatchSize" value="{{this.data.server.episodeBatchSize}}"> <input id="batchSize" matInput placeholder="150" name="MachineIdentifier" [(ngModel)]="this.data.server.episodeBatchSize" value="{{this.data.server.episodeBatchSize}}">
<mat-hint> <mat-hint>
150 by default, you shouldn't need to change this, this sets how many episodes we request from Plex at a single time. 150 by default, you shouldn't need to change this, this sets how many episodes we request from Plex at a single time.
</mat-hint> </mat-hint>
@ -66,7 +66,7 @@
<h2>Libraries</h2> <h2>Libraries</h2>
<div> <div>
<button mat-raised-button (click)="loadLibraries()" <button id="loadLibs" mat-raised-button (click)="loadLibraries()"
class="mat-focus-indicator mat-stroked-button mat-button-base">Load Libraries class="mat-focus-indicator mat-stroked-button mat-button-base">Load Libraries
<i class="fas fa-film"></i> <i class="fas fa-film"></i>
</button> </button>
@ -74,10 +74,10 @@
<div *ngIf="this.data.server.plexSelectedLibraries && this.data.server.plexSelectedLibraries.length > 0"> <div *ngIf="this.data.server.plexSelectedLibraries && this.data.server.plexSelectedLibraries.length > 0">
<label>Please select the libraries for Ombi to monitor. If nothing is selected, Ombi will monitor all <label>Please select the libraries for Ombi to monitor. If nothing is selected, Ombi will monitor all
libraries.</label> libraries.</label>
<div *ngFor="let lib of this.data.server.plexSelectedLibraries"> <div *ngFor="let lib of this.data.server.plexSelectedLibraries; let i = index">
<div class="md-form-field"> <div class="md-form-field">
<div class="checkbox"> <div class="checkbox">
<mat-slide-toggle [(ngModel)]="lib.enabled" [checked]="lib.enabled" <mat-slide-toggle id="lib-{{i}}" [(ngModel)]="lib.enabled" [checked]="lib.enabled"
for="{{lib.title}}">{{lib.title}}</mat-slide-toggle> for="{{lib.title}}">{{lib.title}}</mat-slide-toggle>
</div> </div>
</div> </div>
@ -87,7 +87,7 @@
</mat-dialog-content> </mat-dialog-content>
<mat-dialog-actions align=end> <mat-dialog-actions align=end>
<button style="margin: .5em 0 0 .5em;" align-middle mat-stroked-button color="accent" <button id="testPlexButton" style="margin: .5em 0 0 .5em;" align-middle mat-stroked-button color="accent"
(click)="testPlex()"> (click)="testPlex()">
<span style="display: flex; align-items: baseline; white-space: pre-wrap;"> <span style="display: flex; align-items: baseline; white-space: pre-wrap;">
<i class="fas fa-vial"></i> <i class="fas fa-vial"></i>
@ -95,7 +95,7 @@
</span> </span>
</button> </button>
<button style="margin: .5em 0 0 .5em;" align-middle mat-stroked-button color="warn" <button id="deleteServer" style="margin: .5em 0 0 .5em;" align-middle mat-stroked-button color="warn"
(click)="delete()"> (click)="delete()">
<span style="display: flex; align-items: baseline; white-space: pre-wrap;"> <span style="display: flex; align-items: baseline; white-space: pre-wrap;">
<i class="fas fa-trash"></i> <i class="fas fa-trash"></i>
@ -103,7 +103,7 @@
</span> </span>
</button> </button>
<button style="margin: .5em 0 0 0.5em;" mat-stroked-button color="basic" (click)="cancel()"> <button id="cancel" style="margin: .5em 0 0 0.5em;" mat-stroked-button color="basic" (click)="cancel()">
<span style="display: flex; align-items: baseline; white-space: pre-wrap;"> <span style="display: flex; align-items: baseline; white-space: pre-wrap;">
<i class="fas fa-times"></i> <i class="fas fa-times"></i>
<span> Cancel</span> <span> Cancel</span>
@ -111,7 +111,7 @@
</button> </button>
<button style="margin: .5em 0 0 .5em;" mat-stroked-button color="accent" <button id="saveServer" style="margin: .5em 0 0 .5em;" mat-stroked-button color="accent"
(click)="save()"> (click)="save()">
<span style="display: flex; align-items: baseline; white-space: pre-wrap;"> <span style="display: flex; align-items: baseline; white-space: pre-wrap;">
<i style="vertical-align: text-top;" class="fas fa-check"></i> <i style="vertical-align: text-top;" class="fas fa-check"></i>

@ -37,7 +37,7 @@ export class PlexServerDialogComponent {
public testPlex() { public testPlex() {
this.testerService.plexTest(this.data.server).pipe(take(1)) this.testerService.plexTest(this.data.server).pipe(take(1))
.subscribe(x => { .subscribe(x => {
if (x === true) { if (x) {
this.notificationService.success(`Successfully connected to the Plex server ${this.data.server.name}!`); this.notificationService.success(`Successfully connected to the Plex server ${this.data.server.name}!`);
} else { } else {
this.notificationService.error(`We could not connect to the Plex server ${this.data.server.name}!`); this.notificationService.error(`We could not connect to the Plex server ${this.data.server.name}!`);

@ -30,13 +30,13 @@
<h2 style="margin: 1em 0 0 0;">Servers</h2> <h2 style="margin: 1em 0 0 0;">Servers</h2>
<mat-list style="display:flex; flex-flow: wrap;"> <mat-list style="display:flex; flex-flow: wrap;">
<mat-card class="server-card" *ngFor="let server of settings.servers"> <mat-card class="server-card" *ngFor="let server of settings.servers">
<button mat-button (click)="edit(server)"> <button mat-button (click)="edit(server)" id="{{server.name}}-button">
<h3>{{server.name}}</h3> <h3>{{server.name}}</h3>
</button> </button>
</mat-card> </mat-card>
<mat-card class="server-card new-server-card"> <mat-card class="server-card new-server-card">
<button mat-button (click)="newServer()"> <button mat-button (click)="newServer()" id="newServer">
<i class="fas fa-plus fa-xl"></i> <i class="fas fa-plus fa-xl"></i>
<h3>Manually Add Server</h3> <h3>Manually Add Server</h3>
</button> </button>
@ -114,13 +114,13 @@
<div class="md-form-field col-10"> <div class="md-form-field col-10">
<div *ngIf="!loadedServers"> <div *ngIf="!loadedServers">
<mat-form-field appearance="outline" floatLabel=auto> <mat-form-field appearance="outline" floatLabel=auto>
<input disabled matInput placeholder="No Servers Loaded" id="selectServer-noservers"> <input disabled matInput placeholder="No Servers Loaded" id="servers">
</mat-form-field> </mat-form-field>
</div> </div>
<div *ngIf="loadedServers"> <div *ngIf="loadedServers">
<mat-form-field appearance="outline"> <mat-form-field appearance="outline">
<mat-select placeholder="Servers Loaded! Please Select"> <mat-select placeholder="Servers Loaded! Please Select" id="servers">
<mat-option (click)="selectServer(s)" <mat-option (click)="selectServer(s)"
*ngFor="let s of loadedServers.servers.server" [value]="s.server"> *ngFor="let s of loadedServers.servers.server" [value]="s.server">
{{s.name}}</mat-option> {{s.name}}</mat-option>

@ -86,8 +86,8 @@ export class UserPreferenceComponent implements OnInit {
public languageSelected() { public languageSelected() {
this.identityService.updateLanguage(this.selectedLang).subscribe(_ => { this.identityService.updateLanguage(this.selectedLang).subscribe(_ => {
this.translate.use(this.selectedLang).subscribe();
this.notification.success(this.translate.instant("UserPreferences.Updated")) this.notification.success(this.translate.instant("UserPreferences.Updated"))
this.translate.use(this.selectedLang);
}); });
} }

@ -3,8 +3,7 @@
"node_modules/codelyzer" "node_modules/codelyzer"
], ],
"extends": [ "extends": [
"tslint:recommended", "tslint:recommended"
"tslint-angular"
], ],
"rules": { "rules": {
"max-line-length": [ "max-line-length": [

File diff suppressed because it is too large Load Diff

@ -0,0 +1,45 @@
import { defineConfig } from 'cypress';
import createBundler from "@bahmutov/cypress-esbuild-preprocessor";
import { addCucumberPreprocessorPlugin } from "@badeball/cypress-cucumber-preprocessor";
import createEsbuildPlugin from "@badeball/cypress-cucumber-preprocessor/esbuild";
export default defineConfig({
watchForFileChanges: true,
chromeWebSecurity: false,
viewportWidth: 2560,
viewportHeight: 1440,
retries: {
runMode: 2,
openMode: 0,
},
env: {
username: 'a',
password: 'a',
dockerhost: 'http://172.17.0.1'
},
projectId: 'o5451s',
e2e: {
// We've imported your old cypress plugins here.
// You may want to clean this up later by importing these.
async setupNodeEvents(
on: Cypress.PluginEvents,
config: Cypress.PluginConfigOptions
): Promise<Cypress.PluginConfigOptions> {
await addCucumberPreprocessorPlugin(on, config);
on(
"file:preprocessor",
createBundler({
plugins: [createEsbuildPlugin(config)],
})
);
// Make sure to return the config object as it might have been modified by the plugin.
return config;
// return require('./cypress/plugins/index.js')(on, config)
},
baseUrl: 'http://localhost:5000',
specPattern: ['cypress/tests/**/*.spec.ts*', '**/*.feature'],
excludeSpecPattern: ['**/snapshots/*'],
},
})

@ -1,23 +0,0 @@
{
"$schema": "https://on.cypress.io/cypress.schema.json",
"supportFile": "cypress/support/index.ts",
"baseUrl": "http://localhost:5000",
"integrationFolder": "cypress/tests",
"testFiles": "**/*.spec.ts*",
"watchForFileChanges": true,
"chromeWebSecurity": false,
"viewportWidth": 2560,
"viewportHeight": 1440,
"retries": {
"runMode": 2,
"openMode": 0
},
"ignoreTestFiles": [
"**/snapshots/*"
],
"env": {
"username": "a",
"password": "a"
},
"projectId": "o5451s"
}

@ -0,0 +1,21 @@
Feature: Wizard Setup
Scenario: When visiting Ombi for the first time we should be on the Wizard page
When I visit Ombi
Then I should be on the "Wizard"
Scenario: When navigating through the Wizard feature we are required to create a local user
When I visit Ombi
And I click through all of the pages
And I finish the Wizard
Then I should get a notification "Username '' is invalid, can only contain letters or digits."
And I should be on the User tab
Scenario: Completing the Wizard
When I visit Ombi
And I click through to the user page
And I enter a username
And I enter a password
And I go to the finished tab
And I finish the Wizard
Then I should be on the "login"

@ -0,0 +1,53 @@
import { Given, When, Then } from "@badeball/cypress-cucumber-preprocessor";
import { wizardPage as Page } from "@/integration/page-objects";
Given("I set the Landing Page to {string}", (bool) => {
cy.landingSettings(bool);
});
When("I visit Ombi", () => {
Page.visit();
});
When("I click through all of the pages", () => {
Page.welcomeTab.next.click();
Page.mediaServerTab.next.click();
Page.localUserTab.next.click();
Page.ombiConfigTab.next.click();
});
When("I finish the Wizard", () => {
Page.finishButton.click();
});
When("I click through to the user page", () => {
Page.welcomeTab.next.click();
Page.mediaServerTab.next.click();
});
When("I enter a username", () => {
Page.localUserTab.username.type(Cypress.env("username"));
});
When("I enter a password", () => {
Page.localUserTab.password.type(Cypress.env("password"));
});
When("I go to the finished tab", () => {
Page.localUserTab.next.click();
Page.ombiConfigTab.next.click();
});
Then("I should be on the {string}", (string) => {
cy.location("pathname").should("eq", `/${string}`);
});
Then("I should get a notification {string}", (string) => {
cy.verifyNotification(string);
});
Then("I should be on the User tab", () => {
Page.matStepsHeader.then((_) => {
cy.get('#cdk-step-label-0-2').should('have.attr', 'aria-selected', 'true');
});
});

@ -0,0 +1,13 @@
Feature: Login Page
Scenario: When visiting Ombi and the Landing Page is enabled, we should end up on the landing page
Given I set the Landing Page to "true"
When I visit Ombi
Then I should be on the "landingpage"
Then I click continue
Then I should be on the "login/true"
Scenario: When visiting Ombi and the Landing Page is disabled, we should end up on the login page
Given I set the Landing Page to "false"
When I visit Ombi
Then I should be on the "login"

@ -0,0 +1,19 @@
import { After, Before, Given, When, Then } from "@badeball/cypress-cucumber-preprocessor";
import { loginPage as Page } from "@/integration/page-objects";
Given("I set the Landing Page to {string}", (bool) => {
cy.landingSettings(bool);
});
When("I visit Ombi", () => {
Page.visit();
});
Then("I should be on the {string}", (string) => {
cy.location("pathname").should("eq", `/${string}`);
});
Then("I click continue", () => {
cy.get("[data-cy=continue]").click();
});

@ -0,0 +1,23 @@
{
"request": {
"method": "GET",
"urlPattern": "/library/sections"
},
"response": {
"status": 200,
"jsonBody": {
"mediaContainer": {
"directory":[
{ "key": "1", "title": "lib1"},
{ "key": "2", "title": "lib2"},
{ "key": "3", "title": "lib3"},
{ "key": "4", "title": "lib4"},
{ "key": "5", "title": "lib5"}
]
}
},
"headers": {
"Content-Type": "application/json"
}
}
}

@ -0,0 +1,16 @@
{
"request": {
"method": "GET"
},
"response": {
"status": 200,
"jsonBody": {
"mediaContainer": {
"version": "99"
}
},
"headers": {
"Content-Type": "application/json"
}
}
}

@ -75,8 +75,8 @@ class TvDetailsPage extends BasePage {
return cy.get('#partiallyAvailableBtn'); return cy.get('#partiallyAvailableBtn');
} }
get reportIssueButton(): Cypress.Chainable<any> { reportIssueButton(timeout: number): Cypress.Chainable<any> {
return cy.get('#reportIssueBtn'); return cy.get('#reportIssueBtn', { timeout: timeout});
} }

@ -6,3 +6,5 @@ export * from './search/search.page';
export * from './user-preferences/user-preferences.page'; export * from './user-preferences/user-preferences.page';
export * from './requests/requests.page'; export * from './requests/requests.page';
export * from './details/movies/moviedetails.page'; export * from './details/movies/moviedetails.page';
export * from './settings/settings.page';
export * from './settings/plex/plex-settings.page';

@ -0,0 +1,126 @@
import { BasePage } from "../../base.page";
class PlexCredentials {
get username(): Cypress.Chainable<any> {
return cy.get('#username');
}
get password(): Cypress.Chainable<any> {
return cy.get('#password');
}
get loadServers(): Cypress.Chainable<any> {
return cy.get('#loadServers');
}
get serverDropdown(): Cypress.Chainable<any> {
return cy.get('#servers');
}
}
class PlexServerModal {
get serverName(): Cypress.Chainable<any> {
return cy.get('#serverName');
}
get hostName(): Cypress.Chainable<any> {
return cy.get('#ip');
}
get port(): Cypress.Chainable<any> {
return cy.get('#port');
}
get ssl(): Cypress.Chainable<any> {
return cy.get('#ssl');
}
get authToken(): Cypress.Chainable<any> {
return cy.get('#authToken');
}
get machineIdentifier(): Cypress.Chainable<any> {
return cy.get('#machineId');
}
get externalHostname(): Cypress.Chainable<any> {
return cy.get('#externalHostname');
}
get batchSize(): Cypress.Chainable<any> {
return cy.get('#batchSize');
}
get loadLibraries(): Cypress.Chainable<any> {
return cy.get('#loadLibs');
}
get testButton(): Cypress.Chainable<any> {
return cy.get('#testPlexButton');
}
get deleteButton(): Cypress.Chainable<any> {
return cy.get('#deleteServer');
}
get cancelButton(): Cypress.Chainable<any> {
return cy.get('#cancel');
}
get saveButton(): Cypress.Chainable<any> {
return cy.get('#saveServer');
}
getLib(index: number): Cypress.Chainable<any> {
return cy.get(`#lib-${index}`);
}
}
class PlexServersGrid {
serverCardButton(name: string): Cypress.Chainable<any> {
return cy.get(`#${name}-button`);
}
get newServerButton(): Cypress.Chainable<any> {
return cy.get('#newServer');
}
}
class PlexSettingsPage extends BasePage {
get enableCheckbox(): Cypress.Chainable<any> {
return cy.get('#enable');
}
get enableWatchlist(): Cypress.Chainable<any> {
return cy.get('#enableWatchlistImport');
}
get submit(): Cypress.Chainable<any> {
return cy.get('#save');
}
get fullySync(): Cypress.Chainable<any> {
return cy.get('#fullSync');
}
get partialSync(): Cypress.Chainable<any> {
return cy.get('#recentlyAddedSync');
}
get clearAndResync(): Cypress.Chainable<any> {
return cy.get('#clearData');
}
get runWatchlist(): Cypress.Chainable<any> {
return cy.get('#watchlistImport');
}
plexCredentials = new PlexCredentials();
plexServerModal = new PlexServerModal();
plexServerGrid = new PlexServersGrid();
constructor() {
super();
}
visit(options: Cypress.VisitOptions): Cypress.Chainable<Cypress.AUTWindow>;
visit(): Cypress.Chainable<Cypress.AUTWindow>;
visit(id: string): Cypress.Chainable<Cypress.AUTWindow>;
visit(id: string, options: Cypress.VisitOptions): Cypress.Chainable<Cypress.AUTWindow>;
visit(id?: any, options?: any) {
return cy.visit(`/Settings/Plex`, options);
}
}
export const plexSettingsPage = new PlexSettingsPage();

@ -0,0 +1,20 @@
import { BasePage } from "../base.page";
class SettingsPage extends BasePage {
constructor() {
super();
}
visit(options: Cypress.VisitOptions): Cypress.Chainable<Cypress.AUTWindow>;
visit(): Cypress.Chainable<Cypress.AUTWindow>;
visit(id: string): Cypress.Chainable<Cypress.AUTWindow>;
visit(id: string, options: Cypress.VisitOptions): Cypress.Chainable<Cypress.AUTWindow>;
visit(id?: any, options?: any) {
return cy.visit(`/Settings/About`, options);
}
}
export const settingsPage = new SettingsPage();

@ -12,10 +12,10 @@
// This function is called when a project is opened or re-opened (e.g. due to // This function is called when a project is opened or re-opened (e.g. due to
// the project's config changing) // the project's config changing)
/** /**
* @type {Cypress.PluginConfig} * @type {Cypress.PluginConfig}
*/ */
module.exports = (on, config) => { module.exports = (on, config) => {
// `on` is used to hook into various events Cypress emits
// `config` is the resolved Cypress config
} }

@ -113,7 +113,7 @@ Cypress.Commands.add("getByData", (selector) => {
if (element.fireEvent) { if (element.fireEvent) {
element.fireEvent('on' + event); element.fireEvent('on' + event);
} else { } else {
var evObj = document.createEvent('Events'); const evObj = document.createEvent('Events');
evObj.initEvent(event, true, false); evObj.initEvent(event, true, false);

@ -16,6 +16,8 @@
// Import commands.js using ES2015 syntax: // Import commands.js using ES2015 syntax:
import './commands' import './commands'
import './request.commands'; import './request.commands';
import './plex-settings.commands';
import './mock-data.commands';
import "cypress-real-events/support"; import "cypress-real-events/support";
import '@bahmutov/cy-api/support'; import '@bahmutov/cy-api/support';

@ -0,0 +1,15 @@
Cypress.Commands.add('addMock', (mapping) => {
cy.request({
method: 'POST',
url: 'http://localhost:32400/__admin/mappings',
body: mapping
})
})
Cypress.Commands.add('clearMocks', () => {
cy.request({
method: 'DELETE',
url: 'http://localhost:32400/__admin/mappings'
})
})

@ -0,0 +1,12 @@
Cypress.Commands.add('clearPlexServers', () => {
cy.request({
method: 'POST',
url: '/api/v1/Settings/Plex/',
body: `{"enable":false,"enableWatchlistImport":false,"monitorAll":false,"installId":"0c5c597d-56ea-4f34-8f59-18d34ec82482","servers":[],"id":2}`,
headers: {
'Authorization': 'Bearer ' + window.localStorage.getItem('id_token'),
'Content-Type':"application/json"
}
})
})

@ -1,47 +0,0 @@
import { wizardPage as Page } from "@/integration/page-objects";
describe("Wizard Setup", () => {
it("Wizard should be first page", () => {
Page.visit();
cy.location("pathname").should("contains", "/Wizard");
});
it("Finsh with no local user", () => {
Page.visit();
Page.welcomeTab.next.click();
Page.mediaServerTab.next.click();
Page.localUserTab.next.click();
Page.ombiConfigTab.next.click();
Page.finishButton.click();
cy.verifyNotification("Username '' is invalid, can only contain letters or digits.")
// Verify we end back up on the user page
Page.matStepsHeader.then((items) => {
cy.get('#cdk-step-label-0-2').should('have.attr', 'aria-selected', 'true');
});
});
it("Compete Wizard", () => {
Page.visit();
Page.welcomeTab.next.click();
Page.mediaServerTab.next.click();
Page.localUserTab.username.type(Cypress.env("username"));
Page.localUserTab.password.type(Cypress.env("password"));
Page.localUserTab.next.click();
Page.ombiConfigTab.next.click();
Page.finishButton.click();
cy.location("pathname").should("contains", "/login");
});
});

@ -67,16 +67,13 @@ describe("TV Details Buttons", () => {
}); });
it("Issues Enabled", () => { it.only("Issues Enabled", () => {
cy.intercept("GET", "**/Settings/issuesenabled", 'true').as('issuesEnabled'); cy.intercept("GET", "**/Settings/issuesenabled", 'true').as('issuesEnabled');
cy.visit("/details/tv/1399"); cy.visit("/details/tv/1399");
cy.wait('@issuesEnabled'); cy.wait('@issuesEnabled');
cy.waitUntil(() => { Page.reportIssueButton(10000).should('be.visible');
return Page.reportIssueButton.should("be.visible");
});
Page.reportIssueButton.should('be.visible');
}); });
it("Issues Disabled", () => { it("Issues Disabled", () => {
@ -84,6 +81,6 @@ describe("TV Details Buttons", () => {
Page.visit("1399"); Page.visit("1399");
Page.reportIssueButton.should('not.exist'); Page.reportIssueButton(1000).should('not.exist');
}); });
}); });

@ -17,6 +17,7 @@ describe("Discover Cards Requests Tests", () => {
movie.requested = false; movie.requested = false;
body[0] = movie; body[0] = movie;
console.log('sending res')
res.send(body); res.send(body);
}); });
}).as("cardsResponse"); }).as("cardsResponse");
@ -24,7 +25,7 @@ describe("Discover Cards Requests Tests", () => {
Page.visit(); Page.visit();
cy.wait("@cardsResponse").then((res) => { cy.wait("@cardsResponse").then((res) => {
const body = JSON.parse(res.response.body); const body = res.response.body;
var expectedId = body[0].id; var expectedId = body[0].id;
var title = body[0].title; var title = body[0].title;
@ -76,7 +77,7 @@ describe("Discover Cards Requests Tests", () => {
Page.visit(); Page.visit();
cy.wait("@cardsResponse").then((res) => { cy.wait("@cardsResponse").then((res) => {
const body = JSON.parse(res.response.body); const body = res.response.body
var expectedId = body[6].id; var expectedId = body[6].id;
var title = body[6].title; var title = body[6].title;
@ -119,7 +120,7 @@ describe("Discover Cards Requests Tests", () => {
Page.visit(); Page.visit();
cy.wait("@cardsResponse").then((res) => { cy.wait("@cardsResponse").then((res) => {
const body = JSON.parse(res.response.body); const body = res.response.body
var expectedId = body[1].id; var expectedId = body[1].id;
var title = body[1].title; var title = body[1].title;
@ -151,7 +152,7 @@ describe("Discover Cards Requests Tests", () => {
Page.visit(); Page.visit();
cy.wait("@cardsResponse").then((res) => { cy.wait("@cardsResponse").then((res) => {
const body = JSON.parse(res.response.body); const body = res.response.body
var expectedId = body[1].id; var expectedId = body[1].id;
var title = body[1].title; var title = body[1].title;
@ -183,7 +184,7 @@ describe("Discover Cards Requests Tests", () => {
Page.visit(); Page.visit();
cy.wait("@cardsResponse").then((res) => { cy.wait("@cardsResponse").then((res) => {
const body = JSON.parse(res.response.body); const body = res.response.body
var expectedId = body[1].id; var expectedId = body[1].id;
var title = body[1].title; var title = body[1].title;
@ -220,7 +221,7 @@ describe("Discover Cards Requests Tests", () => {
Page.visit(); Page.visit();
cy.wait("@cardsResponse").then((res) => { cy.wait("@cardsResponse").then((res) => {
const body = JSON.parse(res.response.body); const body = res.response.body
var expectedId = body[1].id; var expectedId = body[1].id;
var title = body[1].title; var title = body[1].title;
@ -284,7 +285,7 @@ describe("Discover Cards Requests Tests", () => {
cy.wait("@otherResponses"); cy.wait("@otherResponses");
cy.wait("@cardsResponse").then((res) => { cy.wait("@cardsResponse").then((res) => {
const body = JSON.parse(res.response.body); const body = res.response.body
var expectedId = body[3].id; var expectedId = body[3].id;
var title = body[3].title; var title = body[3].title;
@ -337,7 +338,7 @@ describe("Discover Cards Requests Tests", () => {
cy.wait("@otherResponses"); cy.wait("@otherResponses");
cy.wait("@cardsResponse").then((res) => { cy.wait("@cardsResponse").then((res) => {
const body = JSON.parse(res.response.body); const body = res.response.body
var expectedId = body[5].id; var expectedId = body[5].id;
var title = body[5].title; var title = body[5].title;

@ -1,32 +1,22 @@
import { loginPage as Page } from "@/integration/page-objects"; import { loginPage as Page } from "@/integration/page-objects";
describe("Login Tests", () => { describe("Login Tests", () => {
it("Landing Page is enabled, should redirect", () => {
cy.landingSettings(true);
Page.visit();
cy.location("pathname").should("eq", "/landingpage");
cy.get("[data-cy=continue]").click();
cy.location("pathname").should("contains", "/login");
});
it("Landing Page is disabled, should not redirect", () => {
cy.landingSettings(false);
Page.visit();
cy.location("pathname").should("eq", "/login");
});
it("Plex OAuth Enabled, should be button", () => { it("Plex OAuth Enabled, should be button", () => {
cy.landingSettings(false); cy.landingSettings(false);
cy.fixture("login/authenticationSettngs").then((settings) => { cy.fixture("login/authenticationSettngs").then((settings) => {
settings.enableOAuth = true; settings.enableOAuth = true;
cy.intercept("GET", "/Settings/Authentication", settings).as( cy.intercept("GET", "api/v1/Settings/Authentication", (req) => {
req.reply((res) => {
res.send(settings);
});
}).as(
"authSettings" "authSettings"
); );
}); });
Page.visit(); Page.visit();
cy.wait("@authSettings");
Page.plexSignInButton.should("be.visible"); Page.plexSignInButton.should("be.visible");
Page.ombiSignInButton.should("be.visible"); Page.ombiSignInButton.should("be.visible");
}); });
@ -35,7 +25,7 @@ describe("Login Tests", () => {
cy.landingSettings(false); cy.landingSettings(false);
cy.fixture("login/authenticationSettngs").then((settings) => { cy.fixture("login/authenticationSettngs").then((settings) => {
settings.enableOAuth = false; settings.enableOAuth = false;
cy.intercept("GET", "/Settings/Authentication", settings).as( cy.intercept("GET", "api/v1//Settings/Authentication", settings).as(
"authSettings" "authSettings"
); );
}); });

@ -24,7 +24,7 @@ describe("Navigation Bar Tests", () => {
cy.removeLogin(); cy.removeLogin();
cy.loginWithCreds(id, "a"); cy.loginWithCreds(id, "a");
cy.intercept("GET", "search/Movie/Popular").as("discoverLoad"); cy.intercept("GET", "api/v2/search/Movie/Popular/**").as("discoverLoad");
Page.visit(); Page.visit();
cy.wait("@discoverLoad"); cy.wait("@discoverLoad");

@ -0,0 +1,182 @@
import { plexSettingsPage as Page } from "@/integration/page-objects";
describe("Plex Settings Tests", () => {
beforeEach(() => {
cy.login();
cy.clearPlexServers();
});
afterEach(() => {
cy.clearMocks();
})
const plexTvApiResponse = `{
"success": true,
"message": null,
"servers": {
"server": [
{
"accessToken": "myaccessToken",
"name": "AutomationServer",
"address": "1.1.1.1",
"port": "32400",
"version": "1.30.0.6442-5070ad484",
"scheme": "http",
"host": "2.2.2.2",
"localAddresses": "${Cypress.env("dockerhost")}",
"machineIdentifier": "9999999999999999",
"createdAt": "5555555555",
"updatedAt": "6666666666",
"owned": "1",
"synced": "0",
"sourceTitle": null,
"ownerId": null,
"home": null
}
],
"friendlyName": "myPlex",
"identifier": "com.plexapp.plugins.myplex",
"machineIdentifier": "3dd86546546546540ff065465460c2654654654654",
"size": "1"
}
}
`;
it("Load Servers from Plex.TV Api and Save", () => {
loadServerFromPlexTvApi();
const modal = Page.plexServerModal;
modal.serverName.should('have.value','AutomationServer');
modal.hostName.should('have.value', Cypress.env("dockerhost"));
modal.port.should('have.value','32400');
modal.authToken.should('have.value','myaccessToken');
modal.machineIdentifier.should('have.value','9999999999999999');
modal.saveButton.click();
Page.plexServerGrid.serverCardButton('AutomationServer').should('be.visible');
Page.submit.click();
cy.wait("@plexSave");
});
it("Load Servers from Plex.TV Api and Edit", () => {
loadServerFromPlexTvApi();
const modal = Page.plexServerModal;
modal.saveButton.click();
Page.plexServerGrid.serverCardButton('AutomationServer').should('be.visible');
Page.submit.click();
cy.wait("@plexSave");
// Edit server
Page.plexServerGrid.serverCardButton('AutomationServer').click();
modal.serverName.should('have.value','AutomationServer');
modal.hostName.should('have.value', Cypress.env("dockerhost"));
modal.port.should('have.value','32400');
modal.authToken.should('have.value','myaccessToken');
modal.machineIdentifier.should('have.value','9999999999999999');
});
it("Load Servers from Plex.TV Api and Test", () => {
cy.fixture('/mocks/plex/plex-test.mock').then((json) => {
cy.addMock(json);
});
loadServerFromPlexTvApi();
cy.intercept("POST", "api/v1/tester/plex", (req) => {
req.reply((res) => {
res.send(plexTvApiResponse);
});
}).as("testResponse");
const modal = Page.plexServerModal;
modal.testButton.click();
cy.wait("@testResponse").then(() => {
cy.contains("Successfully connected to the Plex server AutomationServer");
});
});
it("Load Libraries from New Server", () => {
cy.fixture('/mocks/plex/plex-libraries.mock').then((json) => {
cy.addMock(json);
});
cy.intercept("POST", "api/v1/Plex/Libraries").as("libRequest");
newServer();
const modal = Page.plexServerModal;
modal.loadLibraries.click();
cy.wait("@libRequest");
modal.getLib(0).click();
modal.getLib(0).should('contain.text',"lib1");
});
it("Remove server", () => {
loadServerFromPlexTvApi();
const modal = Page.plexServerModal;
modal.saveButton.click();
newServer(false);
modal.saveButton.click();
Page.plexServerGrid.serverCardButton('AutomationServer').click();
modal.deleteButton.click();
Page.plexServerGrid.serverCardButton('ManualServer').click();
modal.deleteButton.click();
Page.plexServerGrid.serverCardButton('AutomationServer').should('not.exist');
Page.plexServerGrid.serverCardButton('ManualServer').should('not.exist');
});
function loadServerFromPlexTvApi(visitPage = true) {
cy.intercept("POST", "api/v1/Plex/servers", (req) => {
req.reply((res) => {
res.send(plexTvApiResponse);
});
}).as("serverResponse");
cy.intercept("POST", "api/v1/Settings/Plex").as('plexSave');
if (visitPage) {
Page.visit();
}
Page.plexCredentials.username.type('username');
Page.plexCredentials.password.type('password');
Page.plexCredentials.loadServers.click();
cy.wait("@serverResponse");
Page.plexCredentials.serverDropdown.click().get('mat-option').contains('AutomationServer').click();
}
function newServer(visitPage = true) {
if (visitPage) {
Page.visit();
}
Page.plexServerGrid.newServerButton.click();
const modal = Page.plexServerModal;
const server = JSON.parse(plexTvApiResponse);
modal.serverName.clear();
modal.serverName.type("ManualServer");
modal.hostName.type(server.servers.server[0].localAddresses);
modal.port.type(server.servers.server[0].port);
modal.authToken.type(server.servers.server[0].accessToken);
modal.machineIdentifier.type(server.servers.server[0].machineIdentifier);
}
});

@ -12,12 +12,13 @@ const langs = [
]; ];
langs.forEach((l) => { langs.forEach((l) => {
it.only(`Change language to ${l.code}, UI should update`, () => { it.skip(`Change language to ${l.code}, UI should update`, () => {
cy.intercept('POST','**/language').as('langSave'); cy.intercept('POST','**/language').as('langSave');
Page.visit(); Page.visit();
Page.profile.languageSelectBox.click(); Page.profile.languageSelectBox.click();
Page.profile.languageSelectBoxOption(l.code).click(); Page.profile.languageSelectBoxOption(l.code).click();
cy.wait(2000); // wait for UI to update
Page.navbar.discover.contains(l.discover); Page.navbar.discover.contains(l.discover);
cy.wait('@langSave').then((intercept) => { cy.wait('@langSave').then((intercept) => {

@ -1,7 +1,9 @@
{ {
"devDependencies": { "devDependencies": {
"@badeball/cypress-cucumber-preprocessor": "^14.0.0",
"@bahmutov/cy-api": "^1.5.0", "@bahmutov/cy-api": "^1.5.0",
"cypress": "6.8.0", "@bahmutov/cypress-esbuild-preprocessor": "^2.1.5",
"cypress": "12.14.0",
"cypress-wait-until": "^1.7.1", "cypress-wait-until": "^1.7.1",
"typescript": "^4.2.3" "typescript": "^4.2.3"
}, },
@ -17,7 +19,7 @@
"scripts": { "scripts": {
"cypress:version": "cypress version", "cypress:version": "cypress version",
"cypress:verify": "cypress verify", "cypress:verify": "cypress verify",
"cypress:open": "cypress open", "cypress:open": "cypress open --config baseUrl=http://localhost:3577 --env dockerhost=http://localhost",
"cypress:run": "cypress run", "cypress:run": "cypress run",
"types": "tsc --noEmit", "types": "tsc --noEmit",
"e2e": "cypress run", "e2e": "cypress run",
@ -25,6 +27,6 @@
"demo:open": "cypress open --config-file cypress/config/demo.json" "demo:open": "cypress open --config-file cypress/config/demo.json"
}, },
"dependencies": { "dependencies": {
"cypress-real-events": "^1.3.0" "cypress-real-events": "^1.8.1"
} }
} }

@ -1,8 +1,9 @@
{ {
"compilerOptions": { "compilerOptions": {
"target": "es5", "target": "es6",
"esModuleInterop": true,
"lib": ["es2018", "dom"], "lib": ["es2018", "dom"],
"types": ["cypress", "cypress-wait-until", "cypress-image-snapshot", "cypress-real-events", "@bahmutov/cy-api"], "types": ["cypress", "cypress-wait-until", "cypress-image-snapshot", "cypress-real-events", "@bahmutov/cy-api", "node"],
"baseUrl": "./cypress", "baseUrl": "./cypress",
"paths": { "paths": {
"@/*": ["./*"] "@/*": ["./*"]

File diff suppressed because it is too large Load Diff

@ -1,3 +1,3 @@
{ {
"version": "4.42.0" "version": "4.42.1"
} }
Loading…
Cancel
Save