First working POC

This commit is contained in:
2026-01-26 22:21:54 +01:00
commit cfddc1edea
255 changed files with 77606 additions and 0 deletions
+31
View File
@@ -0,0 +1,31 @@
export default class BasePage {
async waitForElement(selector: string, timeout: number = 10000) {
const element = await $(selector);
await element.waitForDisplayed({ timeout });
return element;
}
async clickElement(selector: string) {
const element = await this.waitForElement(selector);
await element.click();
}
async enterText(selector: string, text: string) {
const element = await this.waitForElement(selector);
await element.setValue(text);
}
async getText(selector: string): Promise<string> {
const element = await this.waitForElement(selector);
return await element.getText();
}
async isElementDisplayed(selector: string): Promise<boolean> {
try {
const element = await $(selector);
return await element.isDisplayed();
} catch (error) {
return false;
}
}
}
+55
View File
@@ -0,0 +1,55 @@
import BasePage from "./BasePage";
class HomePage extends BasePage {
// Selectors
get loadingSpinner() {
return $(".animate-spin");
}
get browseLibrariesButton() {
return $("button*=Browse all libraries");
}
get offlineBanner() {
return $(".bg-amber-600\\/90");
}
// Carousel sections
get heroSection() {
return $("div"); // Hero banner would need specific selector
}
// Actions
async waitForHomePageLoad(timeout: number = 15000) {
// Wait for loading spinner to disappear
try {
await this.loadingSpinner.waitForDisplayed({ timeout: 5000 });
await this.loadingSpinner.waitForDisplayed({ timeout, reverse: true });
} catch {
// Spinner might not appear if page loads quickly
}
}
async isOffline(): Promise<boolean> {
try {
return await this.offlineBanner.isDisplayed();
} catch {
return false;
}
}
async clickBrowseLibraries() {
await this.browseLibrariesButton.click();
}
async hasContent(): Promise<boolean> {
// Check if browse button exists (indicates loaded state)
try {
return await this.browseLibrariesButton.isExisting();
} catch {
return false;
}
}
}
export default new HomePage();
+116
View File
@@ -0,0 +1,116 @@
import BasePage from "./BasePage";
class LoginPage extends BasePage {
// Selectors
get pageTitle() {
return $("h1");
}
get serverUrlInput() {
return $("#server-url");
}
get connectButton() {
return $('button[type="submit"]');
}
get usernameInput() {
return $("#username");
}
get passwordInput() {
return $("#password");
}
get signInButton() {
return $('button[type="submit"]');
}
get errorMessage() {
return $(".bg-red-900\\/50");
}
get backButton() {
return $("button*=Back");
}
get serverNameDisplay() {
return $('p.text-\\[var\\(--color-jellyfin\\)\\]');
}
// Actions
async waitForLoginPage(timeout: number = 10000) {
await this.serverUrlInput.waitForDisplayed({ timeout });
}
async enterServerUrl(url: string) {
await this.serverUrlInput.setValue(url);
}
async clickConnect() {
await this.connectButton.click();
}
async connectToServer(url: string) {
await this.enterServerUrl(url);
await this.clickConnect();
// Wait for transition to login form
await this.usernameInput.waitForDisplayed({ timeout: 10000 });
}
async enterUsername(username: string) {
await this.usernameInput.setValue(username);
}
async enterPassword(password: string) {
await this.passwordInput.setValue(password);
}
async clickSignIn() {
await this.signInButton.click();
}
async login(username: string, password: string) {
await this.enterUsername(username);
await this.enterPassword(password);
await this.clickSignIn();
}
async fullLoginFlow(serverUrl: string, username: string, password: string) {
await this.waitForLoginPage();
await this.connectToServer(serverUrl);
await this.login(username, password);
}
async isOnServerStep(): Promise<boolean> {
try {
return await this.serverUrlInput.isDisplayed();
} catch {
return false;
}
}
async isOnLoginStep(): Promise<boolean> {
try {
return await this.usernameInput.isDisplayed();
} catch {
return false;
}
}
async getErrorMessage(): Promise<string> {
await this.errorMessage.waitForDisplayed({ timeout: 5000 });
return await this.errorMessage.getText();
}
async hasError(): Promise<boolean> {
try {
return await this.errorMessage.isDisplayed();
} catch {
return false;
}
}
}
export default new LoginPage();