Compare commits
4
Commits
main
...
v0.6.7-beta
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
38adbf6379 | ||
|
|
94e4d59e42 | ||
|
|
43167d69d3 | ||
|
|
266891fdab |
No files matched your search
@@ -2,6 +2,19 @@
|
||||
|
||||
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.6.7-beta
|
||||
|
||||
**Notes**:
|
||||
- added full tests for single source example.
|
||||
- adds descriptive error if temp dir can't be created on fatal startup
|
||||
- clears temp directory on shutdown.
|
||||
- removed put settings api (unused)
|
||||
- removed more unused config properties.
|
||||
|
||||
**BugFixes**:
|
||||
- fix url encoding issue for search links when theres only one source https://github.com/gtsteffaniak/filebrowser/issues/501
|
||||
- files with # could have problems, double encoded.
|
||||
|
||||
## v0.6.6-beta
|
||||
|
||||
**New Feature**:
|
||||
|
||||
@@ -10,7 +10,7 @@ import { defineConfig, devices } from "@playwright/test";
|
||||
* See https://playwright.dev/docs/test-configuration.
|
||||
*/
|
||||
export default defineConfig({
|
||||
//globalSetup: "./global-setup",
|
||||
globalSetup: "./tests-playwright/noauth-setup",
|
||||
timeout: 5000,
|
||||
testDir: "./tests-playwright/noauth",
|
||||
/* Run tests in files in parallel */
|
||||
@@ -25,6 +25,7 @@ export default defineConfig({
|
||||
reporter: "line",
|
||||
/* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
|
||||
use: {
|
||||
storageState: "noauth.json",
|
||||
actionTimeout: 5000,
|
||||
//storageState: "loginAuth.json",
|
||||
/* Base URL to use in actions like `await page.goto('/')`. */
|
||||
|
||||
@@ -8,6 +8,7 @@ server:
|
||||
config:
|
||||
defaultEnabled: true
|
||||
createUserDir: true
|
||||
|
||||
frontend:
|
||||
name: "Graham's Filebrowser"
|
||||
disableDefaultLinks: true
|
||||
@@ -15,6 +16,7 @@ frontend:
|
||||
- text: "A playwright test"
|
||||
url: "https://playwright.dev/"
|
||||
title: "Playwright"
|
||||
|
||||
auth:
|
||||
signup: false
|
||||
methods:
|
||||
|
||||
+6
-1
@@ -9,6 +9,7 @@ import (
|
||||
|
||||
"github.com/gtsteffaniak/filebrowser/backend/diskcache"
|
||||
"github.com/gtsteffaniak/filebrowser/backend/files"
|
||||
"github.com/gtsteffaniak/filebrowser/backend/fileutils"
|
||||
fbhttp "github.com/gtsteffaniak/filebrowser/backend/http"
|
||||
"github.com/gtsteffaniak/filebrowser/backend/img"
|
||||
"github.com/gtsteffaniak/filebrowser/backend/logger"
|
||||
@@ -101,6 +102,7 @@ func StartFilebrowser() {
|
||||
case <-done:
|
||||
logger.Info("Server stopped unexpectedly. Shutting down...")
|
||||
}
|
||||
fileutils.ClearCacheDir()
|
||||
|
||||
<-shutdownComplete // Ensure we don't exit prematurely
|
||||
// Wait for the server to stop
|
||||
@@ -121,7 +123,10 @@ func rootCMD(ctx context.Context, store *storage.Storage, serverConfig *settings
|
||||
var err error
|
||||
fileCache, err = diskcache.NewFileCache(cacheDir)
|
||||
if err != nil {
|
||||
logger.Fatal(fmt.Sprintf("failed to create file cache: %v", err))
|
||||
if cacheDir == "tmp" {
|
||||
logger.Error("The cache dir could not be created. Make sure the user that you executed the program with has access to create directories in the local path. filebrowser is trying to use the default `server.cacheDir: tmp` , but you can change this location if you need to. Please see configuration wiki for more information about this error. https://github.com/gtsteffaniak/filebrowser/wiki/Configuration")
|
||||
}
|
||||
logger.Fatal(fmt.Sprintf("failed to create file cache path, which is now require to run the server: %v", err))
|
||||
}
|
||||
} else {
|
||||
// No-op cache if no cacheDir is specified
|
||||
|
||||
@@ -71,6 +71,9 @@ func (idx *Index) Search(search string, scope string, sourceSession string) []Se
|
||||
// search files first
|
||||
for _, item := range dir.Files {
|
||||
fullPath := dirName + "/" + item.Name
|
||||
if dirName == "/" {
|
||||
fullPath = dirName + item.Name
|
||||
}
|
||||
value, found := sessionInProgress.Load(sourceSession)
|
||||
if !found || value != runningHash {
|
||||
idx.mu.Unlock()
|
||||
|
||||
@@ -90,7 +90,8 @@ func TestSearchWhileIndexing(t *testing.T) {
|
||||
func TestSearchIndexes(t *testing.T) {
|
||||
index := Index{
|
||||
Directories: map[string]*FileInfo{
|
||||
"/test": {Files: []ItemInfo{{Name: "audio1.wav", Type: "audio"}}},
|
||||
"/": {Files: []ItemInfo{{Name: "audio-one.wav", Type: "audio"}}},
|
||||
"/test": {Files: []ItemInfo{{Name: "audio-one.wav", Type: "audio"}}},
|
||||
"/test/path": {Files: []ItemInfo{{Name: "file.txt", Type: "text"}}},
|
||||
"/new/test": {Files: []ItemInfo{
|
||||
{Name: "audio.wav", Type: "audio"},
|
||||
@@ -230,6 +231,27 @@ func TestSearchIndexes(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
search: "audio",
|
||||
scope: "/",
|
||||
expectedResult: []SearchResult{
|
||||
{
|
||||
Path: "/audio-one.wav",
|
||||
Type: "audio",
|
||||
Size: 0,
|
||||
},
|
||||
{
|
||||
Path: "/test/audio-one.wav",
|
||||
Type: "audio",
|
||||
Size: 0,
|
||||
},
|
||||
{
|
||||
Path: "/new/test/audio.wav",
|
||||
Type: "audio",
|
||||
Size: 0,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"path/filepath"
|
||||
|
||||
"github.com/gtsteffaniak/filebrowser/backend/logger"
|
||||
"github.com/gtsteffaniak/filebrowser/backend/settings"
|
||||
)
|
||||
|
||||
// MoveFile moves a file from src to dst.
|
||||
@@ -175,3 +176,20 @@ func CommonPrefix(sep byte, paths ...string) string {
|
||||
|
||||
return string(c)
|
||||
}
|
||||
|
||||
func ClearCacheDir() {
|
||||
cacheDir := settings.Config.Server.CacheDir
|
||||
entries, err := os.ReadDir(cacheDir)
|
||||
if err != nil {
|
||||
logger.Error(fmt.Sprintf("failed clear cache dir: %v", err))
|
||||
}
|
||||
|
||||
for _, entry := range entries {
|
||||
path := filepath.Join(cacheDir, entry.Name())
|
||||
err = os.RemoveAll(path)
|
||||
if err != nil {
|
||||
logger.Error(fmt.Sprintf("failed clear cache dir: %v", err))
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -114,7 +114,6 @@ func StartHttp(ctx context.Context, Service ImgService, storage *storage.Storage
|
||||
|
||||
// Settings routes
|
||||
api.HandleFunc("GET /settings", withAdmin(settingsGetHandler))
|
||||
api.HandleFunc("PUT /settings", withAdmin(settingsPutHandler))
|
||||
|
||||
api.HandleFunc("GET /onlyoffice/config", withUser(onlyofficeClientConfigGetHandler))
|
||||
api.HandleFunc("POST /onlyoffice/callback", withUser(onlyofficeCallbackHandler))
|
||||
|
||||
@@ -1,20 +1,9 @@
|
||||
package http
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
|
||||
"github.com/gtsteffaniak/filebrowser/backend/settings"
|
||||
)
|
||||
|
||||
type settingsData struct {
|
||||
Signup bool `json:"signup"`
|
||||
CreateUserDir bool `json:"createUserDir"`
|
||||
UserHomeBasePath string `json:"userHomeBasePath"`
|
||||
Defaults settings.UserDefaults `json:"defaults"`
|
||||
Frontend settings.Frontend `json:"frontend"`
|
||||
}
|
||||
|
||||
// settingsGetHandler retrieves the current system settings.
|
||||
// @Summary Get system settings
|
||||
// @Description Returns the current configuration settings for signup, user directories, rules, frontend.
|
||||
@@ -45,29 +34,3 @@ func settingsGetHandler(w http.ResponseWriter, r *http.Request, d *requestContex
|
||||
}
|
||||
return renderJSON(w, r, config)
|
||||
}
|
||||
|
||||
// settingsPutHandler updates the system settings.
|
||||
// @Summary Update system settings
|
||||
// @Description Updates the system configuration settings for signup, user directories, rules, frontend.
|
||||
// @Tags Settings
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param body body settingsData true "Settings data to update"
|
||||
// @Success 200 "Settings updated successfully"
|
||||
// @Failure 400 {object} map[string]string "Bad request - failed to decode body"
|
||||
// @Failure 500 {object} map[string]string "Internal server error"
|
||||
// @Router /api/settings [put]
|
||||
func settingsPutHandler(w http.ResponseWriter, r *http.Request, d *requestContext) (int, error) {
|
||||
req := &settingsData{}
|
||||
err := json.NewDecoder(r.Body).Decode(req)
|
||||
if err != nil {
|
||||
return http.StatusBadRequest, err
|
||||
}
|
||||
|
||||
config.Server.UserHomeBasePath = req.UserHomeBasePath
|
||||
config.UserDefaults = req.Defaults
|
||||
config.Frontend = req.Frontend
|
||||
config.Auth.Signup = req.Signup
|
||||
err = store.Settings.Save(config)
|
||||
return errToStatus(err), err
|
||||
}
|
||||
@@ -5,13 +5,10 @@ import (
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"text/template"
|
||||
|
||||
"github.com/gtsteffaniak/filebrowser/backend/auth"
|
||||
"github.com/gtsteffaniak/filebrowser/backend/logger"
|
||||
"github.com/gtsteffaniak/filebrowser/backend/settings"
|
||||
"github.com/gtsteffaniak/filebrowser/backend/version"
|
||||
)
|
||||
@@ -47,7 +44,6 @@ func handleWithStaticData(w http.ResponseWriter, r *http.Request, file, contentT
|
||||
"DisableExternal": config.Frontend.DisableDefaultLinks,
|
||||
"DisableUsedPercentage": config.Frontend.DisableUsedPercentage,
|
||||
"darkMode": settings.Config.UserDefaults.DarkMode,
|
||||
"Color": config.Frontend.Color,
|
||||
"BaseURL": config.Server.BaseURL,
|
||||
"Version": version.Version,
|
||||
"CommitSHA": version.CommitSHA,
|
||||
@@ -68,19 +64,6 @@ func handleWithStaticData(w http.ResponseWriter, r *http.Request, file, contentT
|
||||
"SourceCount": len(config.Server.Sources),
|
||||
}
|
||||
|
||||
if config.Frontend.Files != "" {
|
||||
fPath := filepath.Join(config.Frontend.Files, "custom.css")
|
||||
_, err := os.Stat(fPath) //nolint:govet
|
||||
|
||||
if err != nil && !os.IsNotExist(err) {
|
||||
logger.Error(fmt.Sprintf("couldn't load custom styles: %v", err))
|
||||
}
|
||||
|
||||
if err == nil {
|
||||
data["CSS"] = true
|
||||
}
|
||||
}
|
||||
|
||||
if config.Auth.Methods.PasswordAuth.Enabled {
|
||||
raw, err := store.Auth.Get("password") //nolint:govet
|
||||
if err != nil {
|
||||
|
||||
@@ -28,9 +28,6 @@ func (s *Storage) Get() (*Settings, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if set.Server.UserHomeBasePath == "" {
|
||||
set.Server.UserHomeBasePath = DefaultUsersHomeBasePath
|
||||
}
|
||||
return set, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -13,24 +13,20 @@ const (
|
||||
)
|
||||
|
||||
type Settings struct {
|
||||
Commands map[string][]string `json:"commands"`
|
||||
Shell []string `json:"shell"`
|
||||
Server Server `json:"server"`
|
||||
Auth Auth `json:"auth"`
|
||||
Frontend Frontend `json:"frontend"`
|
||||
Users []UserDefaults `json:"users,omitempty"`
|
||||
UserDefaults UserDefaults `json:"userDefaults"`
|
||||
Integrations Integrations `json:"integrations"`
|
||||
Server Server `json:"server"`
|
||||
Auth Auth `json:"auth"`
|
||||
Frontend Frontend `json:"frontend"`
|
||||
Users []UserDefaults `json:"users,omitempty"`
|
||||
UserDefaults UserDefaults `json:"userDefaults"`
|
||||
Integrations Integrations `json:"integrations"`
|
||||
}
|
||||
|
||||
type Auth struct {
|
||||
TokenExpirationHours int `json:"tokenExpirationHours"`
|
||||
Recaptcha Recaptcha `json:"recaptcha"`
|
||||
Methods LoginMethods `json:"methods"`
|
||||
Command string `json:"command"`
|
||||
Signup bool `json:"signup"`
|
||||
Method string `json:"method"`
|
||||
Shell string `json:"shell"`
|
||||
Key []byte `json:"key"`
|
||||
AdminUsername string `json:"adminUsername"`
|
||||
AdminPassword string `json:"adminPassword"`
|
||||
@@ -73,17 +69,15 @@ type Server struct {
|
||||
BaseURL string `json:"baseURL"`
|
||||
Logging []LogConfig `json:"logging"`
|
||||
Database string `json:"database"`
|
||||
Root string `json:"root"` // deprecated, use sources
|
||||
UserHomeBasePath string `json:"userHomeBasePath"`
|
||||
Sources []Source `json:"sources"`
|
||||
ExternalUrl string `json:"externalUrl"`
|
||||
InternalUrl string `json:"internalUrl"` // used by integrations
|
||||
CacheDir string `json:"cacheDir"`
|
||||
MaxArchiveSizeGB int64 `json:"maxArchiveSize"`
|
||||
// not exposed to config
|
||||
SourceMap map[string]Source // uses realpath as key
|
||||
NameToSource map[string]Source // uses name as key
|
||||
DefaultSource Source
|
||||
SourceMap map[string]Source `json:"-"` // uses realpath as key
|
||||
NameToSource map[string]Source `json:"-"` // uses name as key
|
||||
DefaultSource Source `json:"-"`
|
||||
}
|
||||
|
||||
type Integrations struct {
|
||||
@@ -135,8 +129,6 @@ type Frontend struct {
|
||||
Name string `json:"name"`
|
||||
DisableDefaultLinks bool `json:"disableDefaultLinks"`
|
||||
DisableUsedPercentage bool `json:"disableUsedPercentage"`
|
||||
Files string `json:"files"`
|
||||
Color string `json:"color"`
|
||||
ExternalLinks []ExternalLink `json:"externalLinks"`
|
||||
}
|
||||
|
||||
|
||||
@@ -917,53 +917,6 @@ const docTemplate = `{
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"put": {
|
||||
"description": "Updates the system configuration settings for signup, user directories, rules, frontend.",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"Settings"
|
||||
],
|
||||
"summary": "Update system settings",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "Settings data to update",
|
||||
"name": "body",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/http.settingsData"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Settings updated successfully"
|
||||
},
|
||||
"400": {
|
||||
"description": "Bad request - failed to decode body",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal server error",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/share": {
|
||||
@@ -1784,9 +1737,6 @@ const docTemplate = `{
|
||||
"settings.Frontend": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"color": {
|
||||
"type": "string"
|
||||
},
|
||||
"disableDefaultLinks": {
|
||||
"type": "boolean"
|
||||
},
|
||||
@@ -1799,9 +1749,6 @@ const docTemplate = `{
|
||||
"$ref": "#/definitions/settings.ExternalLink"
|
||||
}
|
||||
},
|
||||
"files": {
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
}
|
||||
|
||||
@@ -906,53 +906,6 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"put": {
|
||||
"description": "Updates the system configuration settings for signup, user directories, rules, frontend.",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"Settings"
|
||||
],
|
||||
"summary": "Update system settings",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "Settings data to update",
|
||||
"name": "body",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/http.settingsData"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Settings updated successfully"
|
||||
},
|
||||
"400": {
|
||||
"description": "Bad request - failed to decode body",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal server error",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/share": {
|
||||
@@ -1773,9 +1726,6 @@
|
||||
"settings.Frontend": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"color": {
|
||||
"type": "string"
|
||||
},
|
||||
"disableDefaultLinks": {
|
||||
"type": "boolean"
|
||||
},
|
||||
@@ -1788,9 +1738,6 @@
|
||||
"$ref": "#/definitions/settings.ExternalLink"
|
||||
}
|
||||
},
|
||||
"files": {
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
}
|
||||
|
||||
@@ -117,8 +117,6 @@ definitions:
|
||||
type: object
|
||||
settings.Frontend:
|
||||
properties:
|
||||
color:
|
||||
type: string
|
||||
disableDefaultLinks:
|
||||
type: boolean
|
||||
disableUsedPercentage:
|
||||
@@ -127,8 +125,6 @@ definitions:
|
||||
items:
|
||||
$ref: '#/definitions/settings.ExternalLink'
|
||||
type: array
|
||||
files:
|
||||
type: string
|
||||
name:
|
||||
type: string
|
||||
type: object
|
||||
@@ -900,38 +896,6 @@ paths:
|
||||
summary: Get system settings
|
||||
tags:
|
||||
- Settings
|
||||
put:
|
||||
consumes:
|
||||
- application/json
|
||||
description: Updates the system configuration settings for signup, user directories,
|
||||
rules, frontend.
|
||||
parameters:
|
||||
- description: Settings data to update
|
||||
in: body
|
||||
name: body
|
||||
required: true
|
||||
schema:
|
||||
$ref: '#/definitions/http.settingsData'
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: Settings updated successfully
|
||||
"400":
|
||||
description: Bad request - failed to decode body
|
||||
schema:
|
||||
additionalProperties:
|
||||
type: string
|
||||
type: object
|
||||
"500":
|
||||
description: Internal server error
|
||||
schema:
|
||||
additionalProperties:
|
||||
type: string
|
||||
type: object
|
||||
summary: Update system settings
|
||||
tags:
|
||||
- Settings
|
||||
/api/share:
|
||||
get:
|
||||
consumes:
|
||||
|
||||
@@ -211,7 +211,7 @@ export function getDownloadURL(source, path, inline, useExternal) {
|
||||
const apiPath = getApiPath('api/raw', params)
|
||||
if (externalUrl && useExternal) {
|
||||
|
||||
return externalUrl + removePrefix(apiPath,baseURL)
|
||||
return externalUrl + removePrefix(apiPath, baseURL)
|
||||
}
|
||||
return window.origin + apiPath
|
||||
} catch (err) {
|
||||
|
||||
@@ -403,8 +403,17 @@ export default {
|
||||
this.submit();
|
||||
},
|
||||
getRelative(path) {
|
||||
// double encode # to fix issue with # in path
|
||||
// replace all # with %23
|
||||
path = path.replace(/#/g, "%23");
|
||||
|
||||
path = path.slice(1); // remove leading slash
|
||||
const fullpath = "/files/" + this.selectedSource + "/" + encodeURIComponent(path);
|
||||
let fullpath = path;
|
||||
if (state.sources.count === 1) {
|
||||
fullpath = "/files/" + encodeURIComponent(path);
|
||||
} else {
|
||||
fullpath = "/files/" + this.selectedSource + "/" + encodeURIComponent(path);
|
||||
}
|
||||
return fullpath;
|
||||
},
|
||||
getIcon(mimetype) {
|
||||
@@ -430,9 +439,9 @@ export default {
|
||||
return "";
|
||||
}
|
||||
parts.pop();
|
||||
parts = parts.join("/") + "/";
|
||||
parts = parts.join("/");
|
||||
if (isDir) {
|
||||
parts = "/" + parts; // fix weird rtl thing
|
||||
parts = "/" + parts + "/"; // fix weird rtl thing
|
||||
}
|
||||
|
||||
return parts;
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
<h3>{{ $t("settings.userCommands") }}</h3>
|
||||
<p class="small">
|
||||
{{ $t("settings.userCommandsHelp") }} <i>git svn hg</i>.
|
||||
</p>
|
||||
<input class="input input--block" type="text" v-model.trim="raw" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "permissions",
|
||||
props: ["commands"],
|
||||
computed: {
|
||||
raw: {
|
||||
get() {
|
||||
return this.commands.join(" ");
|
||||
},
|
||||
set(value) {
|
||||
if (value !== "") {
|
||||
this.$emit("update:commands", value.split(" "));
|
||||
} else {
|
||||
this.$emit("update:commands", []);
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@@ -22,7 +22,6 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { enableExec } from "@/utils/constants";
|
||||
export default {
|
||||
name: "permissions",
|
||||
props: ["perm"],
|
||||
@@ -41,7 +40,6 @@ export default {
|
||||
this.perm.admin = value;
|
||||
},
|
||||
},
|
||||
isExecEnabled: () => enableExec,
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@@ -101,15 +101,12 @@
|
||||
</p>
|
||||
|
||||
<permissions :perm="localUser.perm" />
|
||||
<commands v-if="isExecEnabled" v-model:commands="user.commands" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Languages from "./Languages.vue";
|
||||
import Permissions from "./Permissions.vue";
|
||||
import Commands from "./Commands.vue";
|
||||
import { enableExec } from "@/utils/constants";
|
||||
import { state } from "@/store";
|
||||
import { settingsApi } from "@/api";
|
||||
|
||||
@@ -118,7 +115,6 @@ export default {
|
||||
components: {
|
||||
Permissions,
|
||||
Languages,
|
||||
Commands,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
@@ -193,9 +189,6 @@ export default {
|
||||
displayHomeDirectoryCheckbox() {
|
||||
return this.isNew && this.createUserDir;
|
||||
},
|
||||
isExecEnabled() {
|
||||
return enableExec; // Removed arrow function
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
setUpdatePassword() {
|
||||
|
||||
@@ -15,7 +15,6 @@ const noAuth = window.FileBrowser.NoAuth;
|
||||
const loginPage = window.FileBrowser.LoginPage;
|
||||
const enableThumbs = window.FileBrowser.EnableThumbs;
|
||||
const resizePreview = window.FileBrowser.ResizePreview;
|
||||
const enableExec = window.FileBrowser.EnableExec;
|
||||
const externalUrl = window.FileBrowser.ExternalUrl
|
||||
const onlyOfficeUrl = window.FileBrowser.OnlyOfficeUrl
|
||||
const serverHasMultipleSources = window.FileBrowser.SourceCount > 1;
|
||||
@@ -47,7 +46,6 @@ export {
|
||||
loginPage,
|
||||
enableThumbs,
|
||||
resizePreview,
|
||||
enableExec,
|
||||
origin,
|
||||
darkMode,
|
||||
settings,
|
||||
|
||||
@@ -66,7 +66,6 @@ export function removePrefix(path, prefix = "") {
|
||||
if (!path.startsWith('/')) {
|
||||
path = '/' + path;
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
|
||||
@@ -42,7 +42,6 @@ import Sidebar from "@/components/sidebar/Sidebar.vue";
|
||||
import ContextMenu from "@/components/ContextMenu.vue";
|
||||
import Notifications from "@/components/Notifications.vue";
|
||||
|
||||
import { enableExec } from "@/utils/constants";
|
||||
import { state, getters, mutations } from "@/store";
|
||||
|
||||
export default {
|
||||
@@ -107,9 +106,6 @@ export default {
|
||||
isDarkMode() {
|
||||
return getters.isDarkMode();
|
||||
},
|
||||
isExecEnabled() {
|
||||
return enableExec;
|
||||
},
|
||||
currentView() {
|
||||
return getters.currentView();
|
||||
},
|
||||
|
||||
@@ -6,7 +6,8 @@
|
||||
</div>
|
||||
|
||||
<div class="card-content">
|
||||
Currently there are no global config options, You can edit the configuration yaml to adjust system settings.
|
||||
Currently there are no global config options, You can edit the configuration yaml to
|
||||
adjust system settings.
|
||||
</div>
|
||||
|
||||
<div class="card-action">
|
||||
@@ -19,7 +20,6 @@
|
||||
import { notify } from "@/notify";
|
||||
import { state, mutations, getters } from "@/store";
|
||||
import { settingsApi } from "@/api";
|
||||
import { enableExec } from "@/utils/constants";
|
||||
import Errors from "@/views/Errors.vue";
|
||||
|
||||
export default {
|
||||
@@ -41,7 +41,6 @@ export default {
|
||||
user() {
|
||||
return state.user;
|
||||
},
|
||||
isExecEnabled: () => enableExec,
|
||||
},
|
||||
async created() {
|
||||
mutations.setLoading("settings", true);
|
||||
|
||||
@@ -10,13 +10,13 @@ async function globalSetup() {
|
||||
await page.getByPlaceholder("Username").fill("admin");
|
||||
await page.getByPlaceholder("Password").fill("admin");
|
||||
await page.getByRole("button", { name: "Login" }).click();
|
||||
await page.waitForURL("**/files/", { timeout: 250 });
|
||||
await page.waitForURL("**/files/", { timeout: 500 });
|
||||
|
||||
const cookies = await context.cookies();
|
||||
expect(cookies.find((c) => c.name === "auth")?.value).toBeDefined();
|
||||
await expect(page).toHaveTitle("Graham's Filebrowser - Files - playwright-files");
|
||||
|
||||
await page.waitForURL("**/files/playwright-files", { timeout: 250 });
|
||||
await page.waitForURL("**/files/playwright-files", { timeout: 500 });
|
||||
|
||||
// Create a share of folder
|
||||
await page.locator('a[aria-label="myfolder"]').waitFor({ state: 'visible' });
|
||||
@@ -36,7 +36,7 @@ async function globalSetup() {
|
||||
localStorage.setItem('shareHash', hash);
|
||||
}, shareHash);
|
||||
|
||||
await page.goto("http://127.0.0.1/files/playwright-files", { timeout: 250 });
|
||||
await page.goto("http://127.0.0.1/files/playwright-files", { timeout: 500 });
|
||||
// Create a share of file
|
||||
await page.locator('a[aria-label="1file1.txt"]').waitFor({ state: 'visible' });
|
||||
await page.locator('a[aria-label="1file1.txt"]').click({ button: "right" });
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
import { Browser, firefox, expect, Page } from "@playwright/test";
|
||||
|
||||
// Perform authentication and store auth state
|
||||
async function globalSetup() {
|
||||
const browser: Browser = await firefox.launch();
|
||||
const context = await browser.newContext();
|
||||
const page: Page = await context.newPage();
|
||||
|
||||
await page.goto("http://127.0.0.1/files", { timeout: 500 });
|
||||
|
||||
// Create a share of folder
|
||||
await page.locator('a[aria-label="myfolder"]').waitFor({ state: 'visible' });
|
||||
await page.locator('a[aria-label="myfolder"]').click({ button: "right" });
|
||||
await page.locator('.selected-count-header').waitFor({ state: 'visible' });
|
||||
await expect(page.locator('.selected-count-header')).toHaveText('1 selected');
|
||||
await page.locator('button[aria-label="Share"]').click();
|
||||
await expect(page.locator('div[aria-label="share-path"]')).toHaveText('Path: /myfolder');
|
||||
await page.locator('button[aria-label="Share-Confirm"]').click();
|
||||
await expect(page.locator("#share .card-content table tbody tr:not(:has(th))")).toHaveCount(1);
|
||||
const shareHash = await page.locator("#share .card-content table tbody tr:not(:has(th)) td").first().textContent();
|
||||
if (!shareHash) {
|
||||
throw new Error("Failed to retrieve shareHash");
|
||||
}
|
||||
// Store shareHash in localStorage
|
||||
await page.evaluate((hash) => {
|
||||
localStorage.setItem('shareHash', hash);
|
||||
}, shareHash);
|
||||
|
||||
await page.goto("http://127.0.0.1/files", { timeout: 500 });
|
||||
// Create a share of file
|
||||
await page.locator('a[aria-label="1file1.txt"]').waitFor({ state: 'visible' });
|
||||
await page.locator('a[aria-label="1file1.txt"]').click({ button: "right" });
|
||||
await page.locator('.selected-count-header').waitFor({ state: 'visible' });
|
||||
await expect(page.locator('.selected-count-header')).toHaveText('1 selected');
|
||||
await page.locator('button[aria-label="Share"]').click();
|
||||
await expect(page.locator('div[aria-label="share-path"]')).toHaveText('Path: /1file1.txt');
|
||||
await page.locator('button[aria-label="Share-Confirm"]').click();
|
||||
await expect(page.locator("#share .card-content table tbody tr:not(:has(th))")).toHaveCount(1);
|
||||
const shareHashFile = await page.locator("#share .card-content table tbody tr:not(:has(th)) td").first().textContent();
|
||||
if (!shareHashFile) {
|
||||
throw new Error("Failed to retrieve shareHash");
|
||||
}
|
||||
// Store shareHash in localStorage
|
||||
await page.evaluate((hash) => {
|
||||
localStorage.setItem('shareHashFile', hash);
|
||||
}, shareHashFile);
|
||||
|
||||
|
||||
await context.storageState({ path: "./noauth.json" });
|
||||
await browser.close();
|
||||
}
|
||||
|
||||
export default globalSetup;
|
||||
@@ -0,0 +1,120 @@
|
||||
import { test, expect } from "../test-setup";
|
||||
|
||||
|
||||
test("info from listing", async({ page, checkForErrors, context }) => {
|
||||
await page.goto("/files/");
|
||||
await expect(page).toHaveTitle("Graham's Filebrowser - Files - playwright-files");
|
||||
await page.locator('a[aria-label="file.tar.gz"]').waitFor({ state: 'visible' });
|
||||
await page.locator('a[aria-label="file.tar.gz"]').click( { button: "right" });
|
||||
await page.locator('.selected-count-header').waitFor({ state: 'visible' });
|
||||
await expect(page.locator('.selected-count-header')).toHaveText('1 selected');
|
||||
await page.locator('button[aria-label="Info"]').click();
|
||||
await expect(page.locator('.break-word')).toHaveText('Display Name: file.tar.gz');
|
||||
checkForErrors();
|
||||
});
|
||||
|
||||
test("info from search", async({ page, checkForErrors, context }) => {
|
||||
await page.goto("/files/");
|
||||
await expect(page).toHaveTitle("Graham's Filebrowser - Files - playwright-files");
|
||||
await page.locator('#search').click()
|
||||
await page.locator('#main-input').fill('file.tar.gz');
|
||||
await expect(page.locator('#result-list')).toHaveCount(1);
|
||||
await page.locator('li[aria-label="file.tar.gz"]').click({ button: "right" });
|
||||
await page.locator('.selected-count-header').waitFor({ state: 'visible' });
|
||||
await expect(page.locator('.selected-count-header')).toHaveText('1 selected');
|
||||
await page.locator('button[aria-label="Info"]').click();
|
||||
await expect(page.locator('.break-word')).toHaveText('Display Name: file.tar.gz');
|
||||
checkForErrors();
|
||||
})
|
||||
|
||||
test("open from search", async({ page, checkForErrors, context }) => {
|
||||
await page.goto("/files/");
|
||||
await expect(page).toHaveTitle("Graham's Filebrowser - Files - playwright-files");
|
||||
await page.locator('#search').click()
|
||||
await page.locator('#main-input').fill('file.tar.gz');
|
||||
await expect(page.locator('#result-list')).toHaveCount(1);
|
||||
await page.locator('li[aria-label="file.tar.gz"]').click();
|
||||
await expect(page).toHaveTitle("Graham's Filebrowser - Files - file.tar.gz");
|
||||
await expect(page.locator('#previewer')).toContainText('Preview is not available for this file.');
|
||||
checkForErrors();
|
||||
})
|
||||
|
||||
test("2x copy from listing to new folder", async({ page, checkForErrors, context }) => {
|
||||
await page.goto("/files/");
|
||||
await expect(page).toHaveTitle("Graham's Filebrowser - Files - playwright-files");
|
||||
await page.locator('a[aria-label="copyme.txt"]').waitFor({ state: 'visible' });
|
||||
await page.locator('a[aria-label="copyme.txt"]').click( { button: "right" });
|
||||
await page.locator('.selected-count-header').waitFor({ state: 'visible' });
|
||||
await expect(page.locator('.selected-count-header')).toHaveText('1 selected');
|
||||
await page.locator('button[aria-label="Copy file"]').click();
|
||||
await expect(page.locator('div[aria-label="filelist-path"]')).toHaveText('Path: /');
|
||||
await expect(page.locator('li[aria-selected="true"]')).toHaveCount(0);
|
||||
await page.locator('li[aria-label="myfolder"]').click();
|
||||
await expect(page.locator('li[aria-selected="true"]')).toHaveCount(1);
|
||||
await page.locator('button[aria-label="Copy"]').click();
|
||||
const popup = page.locator('#popup-notification-content');
|
||||
await popup.waitFor({ state: 'visible' });
|
||||
await expect(popup).toHaveText("Successfully copied file/folder, redirecting...");
|
||||
await page.waitForURL('**/myfolder/');
|
||||
await expect(page).toHaveTitle("Graham's Filebrowser - Files - myfolder");
|
||||
// verify exists and copy again
|
||||
await page.locator('a[aria-label="copyme.txt"]').waitFor({ state: 'visible' });
|
||||
|
||||
// create new directory
|
||||
// Ensure #listingView is visible
|
||||
await page.locator('#listingView').click({ button: "right" });
|
||||
await page.locator('button[aria-label="New folder"]').waitFor({ state: 'visible' });
|
||||
await page.locator('button[aria-label="New folder"]').click();
|
||||
await page.locator('input[aria-label="New Folder Name"]').waitFor({ state: 'visible' });
|
||||
await page.locator('input[aria-label="New Folder Name"]').fill('newfolder');
|
||||
await page.locator('button[aria-label="Create"]').click();
|
||||
|
||||
await expect(page).toHaveTitle("Graham's Filebrowser - Files - newfolder");
|
||||
await page.goBack();
|
||||
await expect(page).toHaveTitle("Graham's Filebrowser - Files - myfolder");
|
||||
|
||||
await page.locator('a[aria-label="copyme.txt"]').click( { button: "right" });
|
||||
await page.locator('.selected-count-header').waitFor({ state: 'visible' });
|
||||
await expect(page.locator('.selected-count-header')).toHaveText('1 selected');
|
||||
await page.locator('button[aria-label="Copy file"]').click();
|
||||
await expect(page.locator('div[aria-label="filelist-path"]')).toHaveText('Path: /myfolder/');
|
||||
await page.locator('li[aria-label="newfolder"]').click();
|
||||
await page.locator('button[aria-label="Copy"]').click();
|
||||
const popup2 = page.locator('#popup-notification-content');
|
||||
await popup2.waitFor({ state: 'visible' });
|
||||
await expect(popup2).toHaveText("Successfully copied file/folder, redirecting...");
|
||||
await page.waitForURL('**/newfolder/');
|
||||
await expect(page).toHaveTitle("Graham's Filebrowser - Files - newfolder");
|
||||
checkForErrors();
|
||||
})
|
||||
|
||||
test("delete file", async({ page, checkForErrors, context }) => {
|
||||
await page.goto("/files/");
|
||||
await expect(page).toHaveTitle("Graham's Filebrowser - Files - playwright-files");
|
||||
await page.locator('a[aria-label="deleteme.txt"]').waitFor({ state: 'visible' });
|
||||
await page.locator('a[aria-label="deleteme.txt"]').click({ button: "right" });
|
||||
await page.locator('.selected-count-header').waitFor({ state: 'visible' });
|
||||
await expect(page.locator('.selected-count-header')).toHaveText('1 selected');
|
||||
await page.locator('button[aria-label="Delete"]').click();
|
||||
await expect( page.locator('.card-content')).toHaveText('Are you sure you want to delete this file/folder?/deleteme.txt');
|
||||
await expect(page.locator('div[aria-label="delete-path"]')).toHaveText('/deleteme.txt');
|
||||
await page.locator('button[aria-label="Confirm-Delete"]').click();
|
||||
const popup = page.locator('#popup-notification-content');
|
||||
await popup.waitFor({ state: 'visible' });
|
||||
await expect(popup).toHaveText("Deleted item successfully! reloading...");
|
||||
checkForErrors();
|
||||
|
||||
})
|
||||
|
||||
test("delete nested file prompt", async({ page, checkForErrors, context }) => {
|
||||
await page.goto("/files/files/folder%23hash/");
|
||||
await expect(page).toHaveTitle("Graham's Filebrowser - Files - folder#hash");
|
||||
await page.locator('a[aria-label="file#.sh"]').waitFor({ state: 'visible' });
|
||||
await page.locator('a[aria-label="file#.sh"]').click({ button: "right" });
|
||||
await page.locator('.selected-count-header').waitFor({ state: 'visible' });
|
||||
await expect(page.locator('.selected-count-header')).toHaveText('1 selected');
|
||||
await page.locator('button[aria-label="Delete"]').click();
|
||||
await expect(page.locator('.card-content')).toHaveText('Are you sure you want to delete this file/folder?/folder#hash/file#.sh');
|
||||
await expect(page.locator('div[aria-label="delete-path"]')).toHaveText('/folder#hash/file#.sh');
|
||||
checkForErrors();
|
||||
})
|
||||
@@ -0,0 +1,60 @@
|
||||
|
||||
import { test, expect } from "../test-setup";
|
||||
|
||||
test("navigate with hash in file name", async({ page, checkForErrors, context }) => {
|
||||
await page.goto("/files/");
|
||||
await expect(page).toHaveTitle("Graham's Filebrowser - Files - playwright-files");
|
||||
await page.locator('a[aria-label="folder#hash"]').waitFor({ state: 'visible' });
|
||||
await page.locator('a[aria-label="folder#hash"]').dblclick();
|
||||
await expect(page).toHaveTitle("Graham's Filebrowser - Files - folder#hash");
|
||||
await page.locator('a[aria-label="file#.sh"]').waitFor({ state: 'visible' });
|
||||
await page.locator('a[aria-label="file#.sh"]').dblclick();
|
||||
await expect(page).toHaveTitle("Graham's Filebrowser - Files - file#.sh");
|
||||
await expect(page.locator('.topTitle')).toHaveText('file#.sh');
|
||||
checkForErrors()
|
||||
})
|
||||
|
||||
test("breadcrumbs navigation checks", async({ page, checkForErrors, context }) => {
|
||||
await page.goto("/files/");
|
||||
await expect(page.locator('a[aria-label="Home"]')).toHaveAttribute("href", "/files/");
|
||||
|
||||
// Ensure no <span> children exist directly under .breadcrumbs (ie no breadcrumbs paths)
|
||||
let spanChildrenCount = await page.locator('.breadcrumbs > span').count();
|
||||
await page.waitForSelector('.breadcrumbs');
|
||||
expect(spanChildrenCount).toBe(0);
|
||||
|
||||
await page.goto("/files/myfolder");
|
||||
await page.waitForSelector('.breadcrumbs');
|
||||
spanChildrenCount = await page.locator('.breadcrumbs > span').count();
|
||||
expect(spanChildrenCount).toBe(1);
|
||||
let breadCrumbLink = page.locator('span[aria-label="breadcrumb-link-myfolder"] a')
|
||||
await expect(breadCrumbLink).toHaveText("myfolder");
|
||||
|
||||
await page.goto("/files/myfolder/testdata");
|
||||
await page.waitForSelector('.breadcrumbs');
|
||||
spanChildrenCount = await page.locator('.breadcrumbs > span').count();
|
||||
expect(spanChildrenCount).toBe(2);
|
||||
breadCrumbLink = page.locator('span[aria-label="breadcrumb-link-testdata"] a')
|
||||
await expect(breadCrumbLink).toHaveText("testdata");
|
||||
|
||||
// TODO: fix this test.. router issue for /files path
|
||||
//await page.goto("/files/files");
|
||||
//await page.waitForSelector('.breadcrumbs');
|
||||
//spanChildrenCount = await page.locator('.breadcrumbs > span').count();
|
||||
//expect(spanChildrenCount).toBe(1);
|
||||
//breadCrumbLink = page.locator('span[aria-label="breadcrumb-link-files"] a')
|
||||
//await expect(breadCrumbLink).toHaveText("files");
|
||||
checkForErrors();
|
||||
});
|
||||
|
||||
test("navigate from search item", async({ page, checkForErrors, context }) => {
|
||||
await page.goto("/files/");
|
||||
await expect(page).toHaveTitle("Graham's Filebrowser - Files - playwright-files");
|
||||
await page.locator('#search').click()
|
||||
await page.locator('#main-input').fill('for testing');
|
||||
await expect(page.locator('#result-list')).toHaveCount(1);
|
||||
await page.locator('li[aria-label="for testing.md"]').click();
|
||||
await expect(page).toHaveTitle("Graham's Filebrowser - Files - for testing.md");
|
||||
await expect(page.locator('.topTitle')).toHaveText('for testing.md');
|
||||
checkForErrors()
|
||||
});
|
||||
@@ -0,0 +1,102 @@
|
||||
|
||||
import { test, expect } from "../test-setup";
|
||||
|
||||
test("breadcrumbs navigation checks", async ({ page, checkForErrors, context }) => {
|
||||
await page.goto("/files/files/");
|
||||
await expect(page).toHaveTitle("Graham's Filebrowser - Files - playwright-files");
|
||||
|
||||
const shareHash = await page.evaluate(() => localStorage.getItem('shareHash'));
|
||||
if (shareHash == "") {
|
||||
throw new Error("Share hash not found in localStorage");
|
||||
}
|
||||
|
||||
await page.goto("/files/share/" + shareHash);
|
||||
await expect(page).toHaveTitle("Graham's Filebrowser - Share - myfolder");
|
||||
await expect(page.locator('a[aria-label="Home"]')).toHaveAttribute("href", `/files/share/${shareHash}/`);
|
||||
|
||||
await page.dblclick('a[aria-label="testdata"]');
|
||||
await expect(page).toHaveTitle("Graham's Filebrowser - Share - testdata");
|
||||
// Ensure no <span> children exist directly under .breadcrumbs (ie no breadcrumbs paths)
|
||||
let spanChildrenCount = await page.locator('.breadcrumbs > span').count();
|
||||
await page.waitForSelector('.breadcrumbs');
|
||||
expect(spanChildrenCount).toBe(1);
|
||||
|
||||
checkForErrors();
|
||||
});
|
||||
|
||||
test("root share path is valid", async ({ page, checkForErrors, context }) => {
|
||||
await page.goto("/files/files/");
|
||||
await expect(page).toHaveTitle("Graham's Filebrowser - Files - playwright-files");
|
||||
await page.locator('button[aria-label="Show-Share"]').waitFor({ state: 'visible' });
|
||||
await page.locator('button[aria-label="Show-Share"]').click();
|
||||
await expect(page.locator('div[aria-label="share-path"]')).toHaveText('Path: /');
|
||||
checkForErrors();
|
||||
});
|
||||
|
||||
test("share file works", async ({ page, checkForErrors, context }) => {
|
||||
await page.goto("/files/files/");
|
||||
await expect(page).toHaveTitle("Graham's Filebrowser - Files - playwright-files");
|
||||
const shareHashFile = await page.evaluate(() => localStorage.getItem('shareHashFile'));
|
||||
if (shareHashFile == "") {
|
||||
throw new Error("Share hash not found in localStorage");
|
||||
}
|
||||
|
||||
await page.goto("/files/share/" + shareHashFile);
|
||||
await expect(page).toHaveTitle("Graham's Filebrowser - Share - 1file1.txt");
|
||||
await expect(page.locator('a[aria-label="Home"]')).toHaveAttribute("href", `/files/share/${shareHashFile}/`);
|
||||
checkForErrors();
|
||||
});
|
||||
|
||||
test("share download single file", async ({ page, checkForErrors, context }) => {
|
||||
await page.goto("/files/files/");
|
||||
await expect(page).toHaveTitle("Graham's Filebrowser - Files - playwright-files");
|
||||
|
||||
const shareHash = await page.evaluate(() => localStorage.getItem('shareHash'));
|
||||
if (shareHash == "") {
|
||||
throw new Error("Share hash not found in localStorage");
|
||||
}
|
||||
|
||||
await page.goto("/files/share/" + shareHash + "/testdata/");
|
||||
await expect(page).toHaveTitle("Graham's Filebrowser - Share - testdata");
|
||||
await page.locator('a[aria-label="gray-sample.jpg"]').click({ button: "right" });
|
||||
await page.locator('button[aria-label="Download"]').waitFor({ state: 'visible' });
|
||||
await page.locator('button[aria-label="Download"]').click();
|
||||
const popup = page.locator('#popup-notification-content');
|
||||
await popup.waitFor({ state: 'visible' });
|
||||
await expect(popup).toHaveText("Downloading...");
|
||||
checkForErrors();
|
||||
});
|
||||
|
||||
test("share download multiple files", async ({ page, checkForErrors, context }) => {
|
||||
await page.goto("/files/files/");
|
||||
await expect(page).toHaveTitle("Graham's Filebrowser - Files - playwright-files");
|
||||
|
||||
const shareHash = await page.evaluate(() => localStorage.getItem('shareHash'));
|
||||
if (shareHash == "") {
|
||||
throw new Error("Share hash not found in localStorage");
|
||||
}
|
||||
|
||||
await page.goto("/files/share/" + shareHash + "/testdata/");
|
||||
await expect(page).toHaveTitle("Graham's Filebrowser - Share - testdata");
|
||||
await page.locator('a[aria-label="gray-sample.jpg"]').click({ button: "right" });
|
||||
await page.locator('button[aria-label="Select multiple"]').waitFor({ state: 'visible' });
|
||||
await page.locator('button[aria-label="Select multiple"]').click();
|
||||
|
||||
const popup = page.locator('#popup-notification-content');
|
||||
await popup.waitFor({ state: 'visible' });
|
||||
await expect(popup).toHaveText("Multiple Selection Enabled");
|
||||
|
||||
await page.locator('a[aria-label="20130612_142406.jpg"]').click();
|
||||
await page.locator('a[aria-label="IMG_2578.JPG"]').click();
|
||||
await page.locator('a[aria-label="gray-sample.jpg"]').click({ button: "right" });
|
||||
|
||||
await page.locator('.selected-count-header').waitFor({ state: 'visible' });
|
||||
await expect(page.locator('.selected-count-header')).toHaveText('3 selected');
|
||||
|
||||
await page.locator('button[aria-label="Download"]').waitFor({ state: 'visible' });
|
||||
await page.locator('button[aria-label="Download"]').click();
|
||||
const popup2 = page.locator('#popup-notification-content');
|
||||
await popup2.waitFor({ state: 'visible' });
|
||||
await expect(popup2).toHaveText("Downloading...");
|
||||
checkForErrors();
|
||||
});
|
||||
@@ -0,0 +1,45 @@
|
||||
import { test, expect } from "../test-setup";
|
||||
|
||||
test("sidebar links", async({ page, checkForErrors, context }) => {
|
||||
await page.goto("/files/");
|
||||
|
||||
// Verify the page title
|
||||
await expect(page).toHaveTitle("Graham's Filebrowser - Files - playwright-files");
|
||||
|
||||
// Locate the credits container
|
||||
const credits = page.locator('.credits'); // Fix the selector to match the HTML structure
|
||||
|
||||
// Assert that the <h3> contains the text 'FileBrowser Quantum'
|
||||
await expect(credits.locator("h4")).toHaveText("Graham's Filebrowser");
|
||||
|
||||
// Assert that the <a> contains the text 'A playwright test'
|
||||
await expect(credits.locator("span").locator("a")).toHaveText('A playwright test');
|
||||
|
||||
// Assert that the <a> does not contain the text 'Help'
|
||||
await expect(credits.locator("span").locator("a")).not.toHaveText('Help');
|
||||
// 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();
|
||||
});
|
||||
@@ -1,13 +1,13 @@
|
||||
SHELL := /bin/bash
|
||||
.SILENT:
|
||||
setup:
|
||||
echo "creating ./backend/test_config.yaml for local testing..." && \
|
||||
echo "creating ./backend/test_config.yaml for local testing..."
|
||||
if [ ! -f backend/test_config.yaml ]; then \
|
||||
cp backend/config.yaml backend/test_config.yaml; \
|
||||
fi
|
||||
echo "installing swagger needed to generate backend api docs..." && \
|
||||
go install github.com/swaggo/swag/cmd/swag@latest && \
|
||||
echo "installing npm requirements for frontend..." && \
|
||||
echo "installing swagger needed to generate backend api docs..."
|
||||
go install github.com/swaggo/swag/cmd/swag@latest
|
||||
echo "installing npm requirements for frontend..."
|
||||
cd frontend && npm i
|
||||
|
||||
update:
|
||||
@@ -30,8 +30,8 @@ run: build-frontend
|
||||
--ldflags="-w -s -X 'github.com/gtsteffaniak/filebrowser/backend/version.CommitSHA=testingCommit' -X 'github.com/gtsteffaniak/filebrowser/backend/version.Version=testing'" . -c test_config.yaml
|
||||
|
||||
build-frontend:
|
||||
cd backend/http && rm -rf dist && rm -rf embed/* && ln -s ../../frontend/dist && \
|
||||
cd ../../frontend && npm run build
|
||||
cd backend/http && rm -rf dist && rm -rf embed/* && ln -s ../../frontend/dist
|
||||
cd frontend && npm run build
|
||||
|
||||
lint-frontend:
|
||||
cd frontend && npm run lint
|
||||
@@ -52,15 +52,10 @@ test-frontend:
|
||||
cd frontend && npm run test
|
||||
|
||||
test-playwright: build-frontend
|
||||
cd backend && GOOS=linux go build -o filebrowser . && cd .. && \
|
||||
docker build -t filebrowser-playwright-tests -f _docker/Dockerfile.playwright-general . && \
|
||||
docker run --rm --name filebrowser-playwright-tests filebrowser-playwright-tests && \
|
||||
docker build -t filebrowser-playwright-tests -f _docker/Dockerfile.playwright-noauth . && \
|
||||
docker run --rm --name filebrowser-playwright-tests filebrowser-playwright-tests && \
|
||||
docker build -t filebrowser-playwright-tests -f _docker/Dockerfile.playwright-proxy . && \
|
||||
cd backend && GOOS=linux go build -o filebrowser .
|
||||
docker build -t filebrowser-playwright-tests -f _docker/Dockerfile.playwright-general .
|
||||
docker run --rm --name filebrowser-playwright-tests filebrowser-playwright-tests
|
||||
|
||||
# Run on a windows machine!
|
||||
release-windows:
|
||||
cd frontend && npm run build-windows && \
|
||||
cd ../backend && go build --ldflags="-w -s -X github.com/gtsteffaniak/filebrowser/backend/version.CommitSHA=' -X 'github.com/gtsteffaniak/filebrowser/backend/version.Version='" .
|
||||
docker build -t filebrowser-playwright-tests -f _docker/Dockerfile.playwright-noauth .
|
||||
docker run --rm --name filebrowser-playwright-tests filebrowser-playwright-tests
|
||||
docker build -t filebrowser-playwright-tests -f _docker/Dockerfile.playwright-proxy .
|
||||
docker run --rm --name filebrowser-playwright-tests filebrowser-playwright-tests
|
||||
Reference in new issue
Block a user