fix save user issue (#1028)
This commit is contained in:
1 parent
120e5f3ca3
commit
482cded2d0
20 files changed
+344
-163
No files matched your search
+87
-59
@@ -8,78 +8,104 @@ on:
|
||||
- "beta/v*.*.*"
|
||||
- "stable/v*.*.*"
|
||||
jobs:
|
||||
prime_docker_cache:
|
||||
name: Prime docker cache
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
- name: Prime Cache
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
context: .
|
||||
file: _docker/Dockerfile.primecache
|
||||
push: false
|
||||
load: false
|
||||
cache-to: |
|
||||
type=gha,mode=max,scope=${{ github.ref_name }}-playwright-base
|
||||
build_frontend:
|
||||
name: Build frontend
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
- name: Build frontend
|
||||
working-directory: frontend
|
||||
run: npm i && npm run build
|
||||
- name: Upload frontend build artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: dist
|
||||
path: frontend/dist
|
||||
build_backend:
|
||||
name: Build backend
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: 'stable'
|
||||
- name: Build backend
|
||||
working-directory: backend
|
||||
run: go build -o filebrowser .
|
||||
- name: Upload backend build artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: filebrowser
|
||||
path: backend/filebrowser
|
||||
test_playwright:
|
||||
name: Test Playwright - general
|
||||
needs: [build_backend, build_frontend, prime_docker_cache]
|
||||
name: Test Playwright - ${{ matrix.name }}
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- name: general
|
||||
dockerfile: ./_docker/Dockerfile.playwright-general
|
||||
- name: proxy
|
||||
dockerfile: ./_docker/Dockerfile.playwright-proxy
|
||||
- name: noauth
|
||||
dockerfile: ./_docker/Dockerfile.playwright-noauth
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
- name: Download frontend build
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: dist
|
||||
path: frontend/dist
|
||||
- name: Download backend build
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: filebrowser
|
||||
path: backend/
|
||||
- name: Make backend executable
|
||||
run: chmod +x backend/filebrowser
|
||||
- name: Copy frontend build to backend
|
||||
run: |
|
||||
mkdir -p backend/http/embed
|
||||
cp -r frontend/dist/* backend/http/embed
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v3.0.0
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3.0.0
|
||||
- uses: actions/setup-node@v4
|
||||
- working-directory: frontend
|
||||
run: npm i && npm run build
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: 'stable'
|
||||
- working-directory: backend
|
||||
run: go build -o filebrowser .
|
||||
- name: Build
|
||||
- name: Build and Test
|
||||
id: build
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
context: .
|
||||
file: ./_docker/Dockerfile.playwright-general
|
||||
push: false
|
||||
test_playwright_proxy:
|
||||
name: Test Playwright - proxy
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v3.0.0
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3.0.0
|
||||
- uses: actions/setup-node@v4
|
||||
- working-directory: frontend
|
||||
run: npm i && npm run build
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: 'stable'
|
||||
- working-directory: backend
|
||||
run: go build -o filebrowser .
|
||||
- name: Build
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
context: .
|
||||
file: ./_docker/Dockerfile.playwright-proxy
|
||||
push: false
|
||||
test_playwright_noauth:
|
||||
name: Test Playwright - noauth
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v3.0.0
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3.0.0
|
||||
- uses: actions/setup-node@v4
|
||||
- working-directory: frontend
|
||||
run: npm i && npm run build
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: 'stable'
|
||||
- working-directory: backend
|
||||
run: go build -o filebrowser .
|
||||
- name: Build
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
context: .
|
||||
file: ./_docker/Dockerfile.playwright-noauth
|
||||
file: ${{ matrix.dockerfile }}
|
||||
push: false
|
||||
load: true
|
||||
cache-from: |
|
||||
type=gha,scope=${{ github.ref_name }}-playwright-base
|
||||
type=gha,scope=main-playwright-base
|
||||
cache-to: |
|
||||
type=gha,mode=max,scope=${{ github.ref_name }}-playwright-base
|
||||
push_pr_to_registry:
|
||||
if: github.event.pull_request.head.repo.fork == false
|
||||
name: Build docker image
|
||||
@@ -112,3 +138,5 @@ jobs:
|
||||
build-args: |
|
||||
VERSION=${{ steps.meta.outputs.version }}
|
||||
REVISION=${{ steps.meta.outputs.revision }}
|
||||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=max
|
||||
@@ -2,6 +2,12 @@
|
||||
|
||||
All notable changes to this project will be documented in this file. For commit guidelines, please refer to [Standard Version](https://github.com/conventional-changelog/standard-version).
|
||||
|
||||
## v0.7.18-beta
|
||||
|
||||
**BugFixes**:
|
||||
- fix lightBackground issue (#1021)
|
||||
- fix user save issues (#1020, #1027)
|
||||
|
||||
## v0.7.17-beta
|
||||
|
||||
See an example of custom css styling that uses the reduce-rounded-corners.css by default and allows users to choose other themes. You can add your own themes as well that users can choose from in profile settings:
|
||||
|
||||
@@ -1,8 +1,17 @@
|
||||
FROM gtstef/playwright-base
|
||||
WORKDIR /app
|
||||
|
||||
# Copy setup scripts first to leverage caching
|
||||
COPY [ "./_docker/src/general/", "./" ]
|
||||
|
||||
WORKDIR /app/frontend
|
||||
COPY [ "./frontend/", "./" ]
|
||||
|
||||
WORKDIR /app/backend/
|
||||
COPY [ "./backend/filebrowser*", "./"]
|
||||
RUN ./filebrowser & sleep 2 && cd ../frontend && npx playwright test
|
||||
RUN mkdir -p ./http/dist
|
||||
COPY [ "./backend/http/embed/", "./http/dist/"]
|
||||
|
||||
# Run tests
|
||||
RUN ./filebrowser set -u testuser1,testuser1
|
||||
RUN FILEBROWSER_NO_EMBEDED=true ./filebrowser & sleep 2 && cd ../frontend && npx playwright test
|
||||
@@ -1,8 +1,16 @@
|
||||
FROM gtstef/playwright-base
|
||||
WORKDIR /app
|
||||
|
||||
# Copy setup scripts first to leverage caching
|
||||
COPY [ "./_docker/src/noauth/", "./" ]
|
||||
|
||||
WORKDIR /app/frontend
|
||||
COPY [ "./frontend/", "./" ]
|
||||
|
||||
WORKDIR /app/backend/
|
||||
COPY [ "./backend/filebrowser", "./"]
|
||||
RUN ./filebrowser & sleep 2 && cd ../frontend && npx playwright test
|
||||
RUN mkdir -p ./http/dist
|
||||
COPY [ "./backend/http/embed/", "./http/dist/"]
|
||||
|
||||
# Run tests
|
||||
RUN FILEBROWSER_NO_EMBEDED=true ./filebrowser & sleep 2 && cd ../frontend && npx playwright test
|
||||
@@ -5,7 +5,9 @@ WORKDIR /app/frontend
|
||||
COPY [ "./frontend/", "./" ]
|
||||
WORKDIR /app/backend/
|
||||
COPY [ "./backend/filebrowser", "./"]
|
||||
RUN mkdir -p ./http/dist
|
||||
COPY [ "./backend/http/embed/", "./http/dist/"]
|
||||
RUN apt update && apt install nginx -y
|
||||
RUN mv default.conf /etc/nginx/conf.d/default.conf
|
||||
RUN sed -i 's/filebrowser/localhost/g' /etc/nginx/conf.d/default.conf
|
||||
RUN nginx & ./filebrowser & sleep 2 && cd ../frontend && npx playwright test
|
||||
RUN nginx & FILEBROWSER_NO_EMBEDED=true ./filebrowser & sleep 2 && cd ../frontend && npx playwright test
|
||||
@@ -0,0 +1 @@
|
||||
FROM gtstef/playwright-base
|
||||
@@ -20,7 +20,7 @@ export default defineConfig({
|
||||
/* Retry on CI only */
|
||||
retries: 2,
|
||||
/* Opt out of parallel tests on CI. */
|
||||
workers: 1, // required for now! todo parallel some tests
|
||||
workers: 2,
|
||||
/* Reporter to use. See https://playwright.dev/docs/test-reporters */
|
||||
reporter: "line",
|
||||
/* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
|
||||
|
||||
+4
-5
@@ -85,10 +85,7 @@ func runCLI() bool {
|
||||
}
|
||||
username := userInfo[0]
|
||||
password := userInfo[1]
|
||||
ok := getStore(dbConfig)
|
||||
if !ok {
|
||||
logger.Fatal("could not load db info")
|
||||
}
|
||||
_ = getStore(dbConfig) // ignore bool check
|
||||
user, err := store.Users.Get(username)
|
||||
if err != nil {
|
||||
newUser := users.User{
|
||||
@@ -113,7 +110,9 @@ func runCLI() bool {
|
||||
} else {
|
||||
logger.Infof("Creating non-admin user: %s\n", username)
|
||||
}
|
||||
err = storage.CreateUser(newUser, asAdmin)
|
||||
newUser.Permissions = settings.Config.UserDefaults.Permissions
|
||||
newUser.Permissions.Admin = asAdmin
|
||||
err = storage.CreateUser(newUser)
|
||||
if err != nil {
|
||||
logger.Errorf("could not create user: %v", err)
|
||||
}
|
||||
|
||||
@@ -234,10 +234,11 @@ func parseFields(user *users.User, fields []string, actorIsAdmin bool) ([]string
|
||||
field := t.Field(i)
|
||||
// which=all can't update password
|
||||
switch strings.ToLower(field.Name) {
|
||||
case "id", "username", "loginmethod", "password", "apikeys", "totpenabled", "totpsecret", "totpnonce":
|
||||
case "id", "username", "password", "apikeys", "totpenabled", "totpsecret", "totpnonce":
|
||||
// Skip these fields
|
||||
continue
|
||||
}
|
||||
|
||||
fields = append(fields, field.Name)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -113,7 +113,7 @@ func quickSetup(store *Storage) {
|
||||
}
|
||||
|
||||
// create new user
|
||||
func CreateUser(userInfo users.User, asAdmin bool) error {
|
||||
func CreateUser(userInfo users.User) error {
|
||||
newUser := &userInfo
|
||||
if userInfo.LoginMethod == "password" {
|
||||
if userInfo.Password == "" {
|
||||
@@ -126,16 +126,11 @@ func CreateUser(userInfo users.User, asAdmin bool) error {
|
||||
}
|
||||
userInfo.Password = hashpass
|
||||
}
|
||||
userInfo.Permissions = settings.Config.UserDefaults.Permissions
|
||||
// must have username
|
||||
if userInfo.Username == "" {
|
||||
return fmt.Errorf("username is required to create a user")
|
||||
}
|
||||
logger.Debugf("Creating user: %v %v", userInfo.Username, userInfo.Scopes)
|
||||
settings.ApplyUserDefaults(newUser)
|
||||
if asAdmin {
|
||||
userInfo.Permissions = settings.AdminPerms()
|
||||
}
|
||||
// create new home directories
|
||||
err := store.Users.Save(newUser, true, false)
|
||||
if err != nil {
|
||||
|
||||
@@ -79,7 +79,8 @@ func setupProxyUser(r *http.Request, data *requestContext, proxyUser string) (*u
|
||||
err = storage.CreateUser(users.User{
|
||||
LoginMethod: users.LoginMethodProxy,
|
||||
Username: proxyUser,
|
||||
}, false)
|
||||
Permissions: settings.Config.UserDefaults.Permissions,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -194,7 +195,8 @@ func signupHandler(w http.ResponseWriter, r *http.Request, d *requestContext) (i
|
||||
},
|
||||
LoginMethod: users.LoginMethodPassword,
|
||||
}
|
||||
err = storage.CreateUser(user, false)
|
||||
user.Permissions = settings.Config.UserDefaults.Permissions
|
||||
err = storage.CreateUser(user)
|
||||
if err != nil {
|
||||
logger.Debug(err.Error())
|
||||
w.WriteHeader(http.StatusConflict)
|
||||
|
||||
@@ -291,10 +291,15 @@ func loginWithOidcUser(w http.ResponseWriter, r *http.Request, username string,
|
||||
if config.Auth.Methods.OidcAuth.AdminGroup == "" {
|
||||
isAdmin = config.UserDefaults.Permissions.Admin
|
||||
}
|
||||
err = storage.CreateUser(users.User{
|
||||
newUser := users.User{
|
||||
LoginMethod: users.LoginMethodOidc,
|
||||
Username: username,
|
||||
}, isAdmin)
|
||||
Permissions: settings.Config.UserDefaults.Permissions,
|
||||
}
|
||||
if isAdmin {
|
||||
newUser.Permissions.Admin = true
|
||||
}
|
||||
err = storage.CreateUser(newUser)
|
||||
if err != nil {
|
||||
return http.StatusInternalServerError, err
|
||||
}
|
||||
|
||||
@@ -164,7 +164,7 @@ func usersPostHandler(w http.ResponseWriter, r *http.Request, d *requestContext)
|
||||
return http.StatusBadRequest, errors.ErrEmptyPassword
|
||||
}
|
||||
|
||||
err = storage.CreateUser(req.User, req.User.Permissions.Admin)
|
||||
err = storage.CreateUser(req.User)
|
||||
if err != nil {
|
||||
return http.StatusInternalServerError, err
|
||||
}
|
||||
|
||||
@@ -36,6 +36,13 @@ export default {
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
$route: {
|
||||
handler(newRoute) {
|
||||
// hide tooltip when route changes
|
||||
this.tooltip.show = false;
|
||||
this.tooltip.content = "";
|
||||
},
|
||||
},
|
||||
tooltip: {
|
||||
handler(newTooltip) {
|
||||
if (newTooltip.show) {
|
||||
|
||||
@@ -8,12 +8,12 @@
|
||||
class="button button--flat button--grey"
|
||||
@click="closeHovers"
|
||||
v-focus
|
||||
:aria-label="$t('buttons.cancel')"
|
||||
aria-label="Cancel"
|
||||
:title="$t('buttons.cancel')"
|
||||
>
|
||||
{{ $t("buttons.cancel") }}
|
||||
</button>
|
||||
<button class="button button--flat" @click="deleteUser">
|
||||
<button class="button button--flat" aria-label="Confirm Delete" @click="deleteUser">
|
||||
{{ $t("buttons.delete") }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@@ -1,60 +1,24 @@
|
||||
import { test, expect } from '../test-setup'
|
||||
|
||||
test('create and delete testuser', async ({
|
||||
page,
|
||||
checkForErrors,
|
||||
context
|
||||
}) => {
|
||||
await page.goto('/settings')
|
||||
await expect(page).toHaveTitle("Graham's Filebrowser - Settings")
|
||||
await page.locator('button[aria-label="Add New User"]').click()
|
||||
await page.locator('#username').fill('testuser')
|
||||
await page.locator('input[aria-label="Password1"]').fill('testpassword')
|
||||
await page.locator('input[aria-label="Password2"]').fill('testpass')
|
||||
// check that the invalid-field class is added properly
|
||||
await expect(page.locator('input[aria-label="Password2"]')).toHaveClass(
|
||||
'input input--block form-form invalid-form'
|
||||
)
|
||||
await page.locator('input[aria-label="Password2"]').fill('testpassword')
|
||||
await page.locator('input[aria-label="Save User"]').click()
|
||||
test("adjusting theme colors", async({ page, checkForErrors, context }) => {
|
||||
await page.goto("/files/");
|
||||
const originalPrimaryColor = await page.evaluate(() => {
|
||||
return getComputedStyle(document.documentElement).getPropertyValue('--primaryColor').trim();
|
||||
});
|
||||
await expect(originalPrimaryColor).toBe('#2196f3');
|
||||
|
||||
// click the edit button for testuser
|
||||
const userRow = page.locator('tr.item', { hasText: 'testuser' })
|
||||
const editLink = await userRow
|
||||
.locator('td[aria-label="Edit User"] a')
|
||||
.getAttribute('href')
|
||||
await page.goto(editLink!)
|
||||
checkForErrors()
|
||||
})
|
||||
|
||||
|
||||
|
||||
test("two factor auth check", async ({ page, checkForErrors, context }) => {
|
||||
// go to settings
|
||||
await page.goto("/settings");
|
||||
await expect(page).toHaveURL(/\/settings/);
|
||||
// click the edit button for testuser
|
||||
const userRow = page.locator('tr.item', { hasText: 'admin' })
|
||||
const editLink = await userRow
|
||||
.locator('td[aria-label="Edit User"] a')
|
||||
.getAttribute('href')
|
||||
await page.goto(editLink!)
|
||||
|
||||
// Toggle the two factor authentication switch
|
||||
const twoFactorCheckbox = page.locator('.toggle-container:has-text("Two-Factor Authentication") input[type="checkbox"]');
|
||||
const twoFactorToggle = page.locator('.toggle-container:has-text("Two-Factor Authentication") label.switch');
|
||||
// Check if it's currently enabled
|
||||
const isChecked = await twoFactorCheckbox.isChecked();
|
||||
// Toggle it by clicking the label (since checkbox is hidden)
|
||||
await twoFactorToggle.click();
|
||||
// Verify it changed state
|
||||
await expect(twoFactorCheckbox).toBeChecked();
|
||||
await page.locator('button[aria-label="Generate Code"]').click();
|
||||
// check for the otp url
|
||||
await expect(page.locator('p[aria-label="otp-url"]')).toBeVisible();
|
||||
|
||||
// check that the otp-url is not empty
|
||||
const otpUrl = await page.locator('p[aria-label="otp-url"]').textContent();
|
||||
expect(otpUrl).not.toBe("");
|
||||
// Verify the page title
|
||||
await expect(page).toHaveTitle("Graham's Filebrowser - Files - playwright-files");
|
||||
await page.locator('i[aria-label="settings"]').click();
|
||||
await expect(page).toHaveTitle("Graham's Filebrowser - Settings");
|
||||
await page.locator('button', { hasText: 'violet' }).click();
|
||||
const popup = page.locator('#popup-notification-content');
|
||||
await popup.waitFor({ state: 'visible' });
|
||||
await expect(popup).toHaveText('Settings updated!');
|
||||
const newPrimaryColor = await page.evaluate(() => {
|
||||
return getComputedStyle(document.documentElement).getPropertyValue('--primaryColor').trim();
|
||||
});
|
||||
await expect(newPrimaryColor).toBe('#9b59b6');
|
||||
// Check for console errors
|
||||
checkForErrors();
|
||||
});
|
||||
@@ -20,26 +20,3 @@ test("sidebar links", async({ page, checkForErrors, context }) => {
|
||||
// Check for console errors
|
||||
checkForErrors();
|
||||
});
|
||||
|
||||
test("adjusting theme colors", async({ page, checkForErrors, context }) => {
|
||||
await page.goto("/files/");
|
||||
const originalPrimaryColor = await page.evaluate(() => {
|
||||
return getComputedStyle(document.documentElement).getPropertyValue('--primaryColor').trim();
|
||||
});
|
||||
await expect(originalPrimaryColor).toBe('#2196f3');
|
||||
|
||||
// Verify the page title
|
||||
await expect(page).toHaveTitle("Graham's Filebrowser - Files - playwright-files");
|
||||
await page.locator('i[aria-label="settings"]').click();
|
||||
await expect(page).toHaveTitle("Graham's Filebrowser - Settings");
|
||||
await page.locator('button', { hasText: 'violet' }).click();
|
||||
const popup = page.locator('#popup-notification-content');
|
||||
await popup.waitFor({ state: 'visible' });
|
||||
await expect(popup).toHaveText('Settings updated!');
|
||||
const newPrimaryColor = await page.evaluate(() => {
|
||||
return getComputedStyle(document.documentElement).getPropertyValue('--primaryColor').trim();
|
||||
});
|
||||
await expect(newPrimaryColor).toBe('#9b59b6');
|
||||
// Check for console errors
|
||||
checkForErrors();
|
||||
});
|
||||
@@ -0,0 +1,177 @@
|
||||
import { test, expect } from "../test-setup";
|
||||
import { Page } from "@playwright/test";
|
||||
|
||||
test('create, check settings, and delete testuser2', async ({
|
||||
page,
|
||||
checkForErrors,
|
||||
}) => {
|
||||
await page.goto('/settings')
|
||||
await expect(page).toHaveTitle("Graham's Filebrowser - Settings")
|
||||
await page.locator('button[aria-label="Add New User"]').click()
|
||||
await page.locator('#username').fill('testuser2')
|
||||
await page.locator('input[aria-label="Password1"]').fill('testpassword')
|
||||
await page.locator('input[aria-label="Password2"]').fill('testpass')
|
||||
// check that the invalid-field class is added properly
|
||||
await expect(page.locator('input[aria-label="Password2"]')).toHaveClass(
|
||||
'input input--block form-form invalid-form'
|
||||
)
|
||||
await page.locator('input[aria-label="Password2"]').fill('testpassword')
|
||||
|
||||
// Just create the user first
|
||||
await page.locator('input[aria-label="Save User"]').click();
|
||||
await page.waitForResponse((resp) => resp.url().includes('/api/users') && resp.status() === 201)
|
||||
|
||||
// We should be back on the settings page
|
||||
await expect(page.locator('tr.item', { hasText: 'testuser2' })).toBeVisible();
|
||||
|
||||
// Now, click the edit button for testuser2
|
||||
const userRow = page.locator('tr.item', { hasText: 'testuser2' })
|
||||
const editLink = await userRow
|
||||
.locator('td[aria-label="Edit User"] a')
|
||||
.getAttribute('href')
|
||||
await page.goto(editLink!)
|
||||
|
||||
// Now on the edit page, toggle the settings
|
||||
const settingsToToggle = [
|
||||
"Administrator",
|
||||
"Prevent the user from changing the password",
|
||||
"Edit files",
|
||||
"Share files",
|
||||
"Create and manage long-live API keys",
|
||||
"Enable real-time connections and updates",
|
||||
];
|
||||
|
||||
for (const settingName of settingsToToggle) {
|
||||
const toggleContainer = page.locator(".toggle-container", { hasText: settingName });
|
||||
const toggleSwitch = toggleContainer.locator("label.switch");
|
||||
await toggleSwitch.click();
|
||||
}
|
||||
|
||||
// Save the updated settings
|
||||
await page.locator('input[aria-label="Save User"]').click();
|
||||
|
||||
// The app might show a notification and stay on the page. Let's reload to ensure we have the latest state.
|
||||
await page.reload({ waitUntil: 'networkidle' });
|
||||
|
||||
for (const settingName of settingsToToggle) {
|
||||
const checkbox = page.locator(`.toggle-container:has-text("${settingName}") input[type="checkbox"]`);
|
||||
await expect(checkbox).toBeChecked();
|
||||
}
|
||||
|
||||
// Delete the user
|
||||
await page.locator('button[aria-label="Delete User"]').click();
|
||||
await page.locator('button[aria-label="Confirm Delete"]').click();
|
||||
|
||||
// After deletion, we should be back on the settings page.
|
||||
await expect(page.locator('tr.item', { hasText: 'testuser2' })).not.toBeVisible();
|
||||
checkForErrors()
|
||||
})
|
||||
|
||||
test("two factor auth check", async ({ page, checkForErrors, context }) => {
|
||||
// go to settings
|
||||
await page.goto("/settings");
|
||||
await expect(page).toHaveURL(/\/settings/);
|
||||
// click the edit button for testuser
|
||||
const userRow = page.locator('tr.item', { hasText: 'admin' })
|
||||
const editLink = await userRow
|
||||
.locator('td[aria-label="Edit User"] a')
|
||||
.getAttribute('href')
|
||||
await page.goto(editLink!)
|
||||
|
||||
// Toggle the two factor authentication switch
|
||||
const twoFactorCheckbox = page.locator('.toggle-container:has-text("Two-Factor Authentication") input[type="checkbox"]');
|
||||
const twoFactorToggle = page.locator('.toggle-container:has-text("Two-Factor Authentication") label.switch');
|
||||
// Check if it's currently enabled
|
||||
const isChecked = await twoFactorCheckbox.isChecked();
|
||||
// Toggle it by clicking the label (since checkbox is hidden)
|
||||
await twoFactorToggle.click();
|
||||
// Verify it changed state
|
||||
await expect(twoFactorCheckbox).toBeChecked();
|
||||
await page.locator('button[aria-label="Generate Code"]').click();
|
||||
// check for the otp url
|
||||
await expect(page.locator('p[aria-label="otp-url"]')).toBeVisible();
|
||||
|
||||
// check that the otp-url is not empty
|
||||
const otpUrl = await page.locator('p[aria-label="otp-url"]').textContent();
|
||||
expect(otpUrl).not.toBe("");
|
||||
checkForErrors();
|
||||
});
|
||||
|
||||
test.describe("User Settings Persistence", () => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await page.goto("/settings");
|
||||
const userRow = page.locator("tr.item", { hasText: "testuser1" });
|
||||
const editLink = await userRow
|
||||
.locator('td[aria-label="Edit User"] a')
|
||||
.getAttribute("href");
|
||||
await page.goto(editLink!);
|
||||
});
|
||||
|
||||
async function toggleSettingAndSave(page: Page, settingName: string) {
|
||||
const toggleContainer = page.locator(".toggle-container", { hasText: settingName });
|
||||
const toggleSwitch = toggleContainer.locator("label.switch");
|
||||
await toggleSwitch.click();
|
||||
await page.locator('input[aria-label="Save User"]').click();
|
||||
await page.reload();
|
||||
}
|
||||
|
||||
test('should persist "Prevent the user from changing the password" setting', async ({ page }) => {
|
||||
const settingName = "Prevent the user from changing the password";
|
||||
const checkbox = page.locator(`.toggle-container:has-text("${settingName}") input[type="checkbox"]`);
|
||||
await expect(checkbox).not.toBeChecked();
|
||||
await toggleSettingAndSave(page, settingName);
|
||||
await expect(checkbox).toBeChecked();
|
||||
});
|
||||
|
||||
test('should persist "Administrator" setting', async ({ page }) => {
|
||||
const settingName = "Administrator";
|
||||
const checkbox = page.locator(`.toggle-container:has-text("${settingName}") input[type="checkbox"]`);
|
||||
await expect(checkbox).not.toBeChecked(); // Admin user should have this on
|
||||
await toggleSettingAndSave(page, settingName);
|
||||
await expect(checkbox).toBeChecked();
|
||||
});
|
||||
|
||||
test('should persist "Edit files" setting', async ({ page }) => {
|
||||
const settingName = "Edit files";
|
||||
const checkbox = page.locator(`.toggle-container:has-text("${settingName}") input[type="checkbox"]`);
|
||||
await expect(checkbox).not.toBeChecked();
|
||||
await toggleSettingAndSave(page, settingName);
|
||||
await expect(checkbox).toBeChecked();
|
||||
await toggleSettingAndSave(page, settingName); // Revert
|
||||
});
|
||||
|
||||
test('should persist "Share files" setting', async ({ page }) => {
|
||||
const settingName = "Share files";
|
||||
const checkbox = page.locator(`.toggle-container:has-text("${settingName}") input[type="checkbox"]`);
|
||||
await expect(checkbox).not.toBeChecked();
|
||||
await toggleSettingAndSave(page, settingName);
|
||||
await expect(checkbox).toBeChecked();
|
||||
});
|
||||
|
||||
test('should persist "Create and manage long-live API keys" setting', async ({ page }) => {
|
||||
const settingName = "Create and manage long-live API keys";
|
||||
const checkbox = page.locator(`.toggle-container:has-text("${settingName}") input[type="checkbox"]`);
|
||||
await expect(checkbox).not.toBeChecked();
|
||||
await toggleSettingAndSave(page, settingName);
|
||||
await expect(checkbox).toBeChecked();
|
||||
});
|
||||
|
||||
test('should persist "Enable real-time connections and updates" setting', async ({ page }) => {
|
||||
const settingName = "Enable real-time connections and updates";
|
||||
const checkbox = page.locator(`.toggle-container:has-text("${settingName}") input[type="checkbox"]`);
|
||||
await expect(checkbox).not.toBeChecked();
|
||||
await toggleSettingAndSave(page, settingName);
|
||||
await expect(checkbox).toBeChecked();
|
||||
});
|
||||
|
||||
test('should persist "allowed login method" setting', async ({ page }) => {
|
||||
const loginMethodSelector = page.locator("#loginMethod");
|
||||
await expect(loginMethodSelector).toHaveValue("password");
|
||||
|
||||
await loginMethodSelector.selectOption({ label: "Proxy" });
|
||||
await page.locator('input[aria-label="Save User"]').click();
|
||||
await page.reload();
|
||||
|
||||
await expect(page.locator("#loginMethod")).toHaveValue("proxy");
|
||||
});
|
||||
});
|
||||
@@ -55,7 +55,6 @@ async function globalSetup() {
|
||||
localStorage.setItem('shareHashFile', hash);
|
||||
}, shareHashFile);
|
||||
|
||||
|
||||
await context.storageState({ path: "./loginAuth.json" });
|
||||
await browser.close();
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
"allowJs": true,
|
||||
"target": "ESNext",
|
||||
"useDefineForClassFields": true,
|
||||
"noEmit": true,
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "Node10",
|
||||
"strict": true,
|
||||
@@ -18,8 +19,8 @@
|
||||
"paths": {
|
||||
"@/*": ["./src/*"]
|
||||
},
|
||||
"checkJs": false, // check later
|
||||
"checkJs": true,
|
||||
},
|
||||
"include": ["src/**/*.ts", "src/**/*.js", "src/**/*.vue", "tests-playwright/general/auth.spec.ts"],
|
||||
"include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.vue", "tests-playwright/**/*.ts", "../_docker/src/proxy/frontend/playwright.config.ts"],
|
||||
"exclude": ["node_modules", "dist"]
|
||||
}
|
||||
Reference in new issue
Block a user