diff --git a/flake.lock b/flake.lock
index 203568a..d052ebf 100644
--- a/flake.lock
+++ b/flake.lock
@@ -2,11 +2,11 @@
"nodes": {
"nixpkgs": {
"locked": {
- "lastModified": 1726463316,
- "narHash": "sha256-gI9kkaH0ZjakJOKrdjaI/VbaMEo9qBbSUl93DnU7f4c=",
+ "lastModified": 1729256560,
+ "narHash": "sha256-/uilDXvCIEs3C9l73JTACm4quuHUsIHcns1c+cHUJwA=",
"owner": "NixOS",
"repo": "nixpkgs",
- "rev": "99dc8785f6a0adac95f5e2ab05cc2e1bf666d172",
+ "rev": "4c2fcb090b1f3e5b47eaa7bd33913b574a11e0a0",
"type": "github"
},
"original": {
diff --git a/rest-api/db/migrations/20240810115846_image_upload_function.sql b/rest-api/db/migrations/20240810115846_image_upload_function.sql
index 4c34969..a3e9495 100644
--- a/rest-api/db/migrations/20240810115846_image_upload_function.sql
+++ b/rest-api/db/migrations/20240810115846_image_upload_function.sql
@@ -6,13 +6,31 @@ AS $$
DECLARE
_headers JSON := CURRENT_SETTING('request.headers', TRUE)::JSON;
_website_id UUID := (_headers ->> 'x-website-id')::UUID;
- _mimetype TEXT := _headers ->> 'x-mimetype';
_original_filename TEXT := _headers ->> 'x-original-filename';
_allowed_mimetypes TEXT[] := ARRAY['image/png', 'image/jpeg', 'image/webp', 'image/avif', 'image/gif', 'image/svg+xml'];
_max_file_size BIGINT := 5 * 1024 * 1024;
_has_access BOOLEAN;
+ _mimetype TEXT;
BEGIN
_has_access = internal.user_has_website_access (_website_id, 20);
+ _mimetype := CASE WHEN SUBSTRING($1 FROM 1 FOR 8) = '\x89504E470D0A1A0A'::BYTEA THEN
+ 'image/png'
+ WHEN SUBSTRING($1 FROM 1 FOR 3) = '\xFFD8FF'::BYTEA THEN
+ 'image/jpeg'
+ WHEN SUBSTRING($1 FROM 1 FOR 4) = '\x52494646'::BYTEA
+ AND SUBSTRING($1 FROM 9 FOR 4) = '\x57454250'::BYTEA THEN
+ 'image/webp'
+ WHEN SUBSTRING($1 FROM 5 FOR 7) = '\x66747970617669'::BYTEA THEN
+ 'image/avif'
+ WHEN SUBSTRING($1 FROM 1 FOR 6) = '\x474946383761'::BYTEA
+ OR SUBSTRING($1 FROM 1 FOR 6) = '\x474946383961'::BYTEA THEN
+ 'image/gif'
+ WHEN SUBSTRING($1 FROM 1 FOR 5) = '\x3C3F786D6C'::BYTEA
+ OR SUBSTRING($1 FROM 1 FOR 4) = '\x3C737667'::BYTEA THEN
+ 'image/svg+xml'
+ ELSE
+ NULL
+ END;
IF OCTET_LENGTH($1) = 0 THEN
RAISE invalid_parameter_value
USING message = 'No file data was provided';
diff --git a/rest-api/db/migrations/20240911070907_change_log.sql b/rest-api/db/migrations/20240911070907_change_log.sql
index aa3c23b..580f03e 100644
--- a/rest-api/db/migrations/20240911070907_change_log.sql
+++ b/rest-api/db/migrations/20240911070907_change_log.sql
@@ -36,6 +36,7 @@ CREATE FUNCTION internal.track_changes ()
DECLARE
_website_id UUID;
_user_id UUID := (CURRENT_SETTING('request.jwt.claims', TRUE)::JSON ->> 'user_id')::UUID;
+ _new_value HSTORE;
BEGIN
IF (NOT EXISTS (
SELECT
@@ -43,7 +44,7 @@ BEGIN
FROM
internal.user AS u
WHERE
- u.id = _user_id) OR (to_jsonb (OLD.*) - 'last_modified_at' - 'last_modified_by') = (to_jsonb (NEW.*) - 'last_modified_at' - 'last_modified_by')) THEN
+ u.id = _user_id) OR REGEXP_REPLACE((to_jsonb (OLD.*) - 'last_modified_at' - 'last_modified_by')::TEXT, '\r\n|\r', '\n', 'g') = REGEXP_REPLACE((to_jsonb (NEW.*) - 'last_modified_at' - 'last_modified_by')::TEXT, '\r\n|\r', '\n', 'g')) THEN
RETURN NULL;
END IF;
IF TG_TABLE_NAME = 'website' THEN
@@ -52,8 +53,13 @@ BEGIN
_website_id := COALESCE(NEW.website_id, OLD.website_id);
END IF;
IF TG_OP = 'INSERT' THEN
+ _new_value := CASE WHEN TG_TABLE_NAME = 'media' THEN
+ HSTORE (NEW) - 'blob'::TEXT
+ ELSE
+ HSTORE (NEW)
+ END;
INSERT INTO internal.change_log (website_id, table_name, operation, new_value)
- VALUES (_website_id, TG_TABLE_NAME, TG_OP, HSTORE (NEW));
+ VALUES (_website_id, TG_TABLE_NAME, TG_OP, _new_value);
ELSIF (TG_OP = 'UPDATE'
AND EXISTS (
SELECT
@@ -86,6 +92,11 @@ CREATE TRIGGER track_changes_website
FOR EACH ROW
EXECUTE FUNCTION internal.track_changes ();
+CREATE TRIGGER track_changes_media
+ AFTER INSERT ON internal.media
+ FOR EACH ROW
+ EXECUTE FUNCTION internal.track_changes ();
+
CREATE TRIGGER track_changes_settings
AFTER UPDATE ON internal.settings
FOR EACH ROW
@@ -129,6 +140,8 @@ CREATE TRIGGER track_changes_collab
-- migrate:down
DROP TRIGGER track_changes_website ON internal.website;
+DROP TRIGGER track_changes_media ON internal.media;
+
DROP TRIGGER track_changes_settings ON internal.settings;
DROP TRIGGER track_changes_header ON internal.header;
diff --git a/rest-api/db/migrations/20241011092744_filesystem_triggers.sql b/rest-api/db/migrations/20241011092744_filesystem_triggers.sql
index 0369006..7c396f5 100644
--- a/rest-api/db/migrations/20241011092744_filesystem_triggers.sql
+++ b/rest-api/db/migrations/20241011092744_filesystem_triggers.sql
@@ -5,27 +5,29 @@ CREATE FUNCTION internal.cleanup_filesystem ()
DECLARE
_website_id UUID;
_domain_prefix VARCHAR(16);
+ _base_path CONSTANT TEXT := '/var/www/archtika-websites/';
+ _preview_path TEXT;
+ _prod_path TEXT;
BEGIN
IF TG_TABLE_NAME = 'website' THEN
_website_id := OLD.id;
- SELECT
- d.prefix INTO _domain_prefix
- FROM
- internal.domain_prefix AS d
- WHERE
- d.website_id = _website_id;
- EXECUTE FORMAT('COPY (SELECT '''') TO PROGRAM ''rm -rf /var/www/archtika-websites/previews/%s''', _website_id);
- EXECUTE FORMAT('COPY (SELECT '''') TO PROGRAM ''rm -rf /var/www/archtika-websites/%s''', COALESCE(_domain_prefix, _website_id::VARCHAR));
ELSE
_website_id := OLD.website_id;
- SELECT
- d.prefix INTO _domain_prefix
- FROM
- internal.domain_prefix AS d
- WHERE
- d.website_id = _website_id;
- EXECUTE FORMAT('COPY (SELECT '''') TO PROGRAM ''rm -rf /var/www/archtika-websites/previews/%s/legal-information.html''', _website_id);
- EXECUTE FORMAT('COPY (SELECT '''') TO PROGRAM ''rm -rf /var/www/archtika-websites/%s/legal-information.html''', COALESCE(_domain_prefix, _website_id::VARCHAR));
+ END IF;
+ SELECT
+ d.prefix INTO _domain_prefix
+ FROM
+ internal.domain_prefix d
+ WHERE
+ d.website_id = _website_id;
+ _preview_path := _base_path || 'previews/' || _website_id;
+ _prod_path := _base_path || COALESCE(_domain_prefix, _website_id::TEXT);
+ IF TG_TABLE_NAME = 'website' THEN
+ EXECUTE FORMAT('COPY (SELECT '''') TO PROGRAM ''rm -rf %s''', _preview_path);
+ EXECUTE FORMAT('COPY (SELECT '''') TO PROGRAM ''rm -rf %s''', _prod_path);
+ ELSE
+ EXECUTE FORMAT('COPY (SELECT '''') TO PROGRAM ''rm -f %s/legal-information.html''', _preview_path);
+ EXECUTE FORMAT('COPY (SELECT '''') TO PROGRAM ''rm -f %s/legal-information.html''', _prod_path);
END IF;
RETURN OLD;
END;
diff --git a/web-app/package-lock.json b/web-app/package-lock.json
index ac3a952..7ef462a 100644
--- a/web-app/package-lock.json
+++ b/web-app/package-lock.json
@@ -15,7 +15,7 @@
"marked-highlight": "2.1.4"
},
"devDependencies": {
- "@playwright/test": "1.46.0",
+ "@playwright/test": "1.47.0",
"@sveltejs/adapter-auto": "3.2.5",
"@sveltejs/adapter-node": "5.2.3",
"@sveltejs/kit": "2.5.28",
@@ -765,13 +765,13 @@
}
},
"node_modules/@playwright/test": {
- "version": "1.46.0",
- "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.46.0.tgz",
- "integrity": "sha512-/QYft5VArOrGRP5pgkrfKksqsKA6CEFyGQ/gjNe6q0y4tZ1aaPfq4gIjudr1s3D+pXyrPRdsy4opKDrjBabE5w==",
+ "version": "1.47.0",
+ "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.47.0.tgz",
+ "integrity": "sha512-SgAdlSwYVpToI4e/IH19IHHWvoijAYH5hu2MWSXptRypLSnzj51PcGD+rsOXFayde4P9ZLi+loXVwArg6IUkCA==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
- "playwright": "1.46.0"
+ "playwright": "1.47.0"
},
"bin": {
"playwright": "cli.js"
@@ -3655,13 +3655,13 @@
}
},
"node_modules/playwright": {
- "version": "1.46.0",
- "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.46.0.tgz",
- "integrity": "sha512-XYJ5WvfefWONh1uPAUAi0H2xXV5S3vrtcnXe6uAOgdGi3aSpqOSXX08IAjXW34xitfuOJsvXU5anXZxPSEQiJw==",
+ "version": "1.47.0",
+ "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.47.0.tgz",
+ "integrity": "sha512-jOWiRq2pdNAX/mwLiwFYnPHpEZ4rM+fRSQpRHwEwZlP2PUANvL3+aJOF/bvISMhFD30rqMxUB4RJx9aQbfh4Ww==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
- "playwright-core": "1.46.0"
+ "playwright-core": "1.47.0"
},
"bin": {
"playwright": "cli.js"
@@ -3674,9 +3674,9 @@
}
},
"node_modules/playwright-core": {
- "version": "1.46.0",
- "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.46.0.tgz",
- "integrity": "sha512-9Y/d5UIwuJk8t3+lhmMSAJyNP1BUC/DqP3cQJDQQL/oWqAiuPTLgy7Q5dzglmTLwcBRdetzgNM/gni7ckfTr6A==",
+ "version": "1.47.0",
+ "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.47.0.tgz",
+ "integrity": "sha512-1DyHT8OqkcfCkYUD9zzUTfg7EfTd+6a8MkD/NWOvjo0u/SCNd5YmY/lJwFvUZOxJbWNds+ei7ic2+R/cRz/PDg==",
"dev": true,
"license": "Apache-2.0",
"bin": {
diff --git a/web-app/package.json b/web-app/package.json
index 36386a7..8715dbd 100644
--- a/web-app/package.json
+++ b/web-app/package.json
@@ -14,7 +14,7 @@
"gents": "pg-to-ts generate -c postgres://postgres@localhost:15432/archtika -o src/lib/db-schema.ts -s internal"
},
"devDependencies": {
- "@playwright/test": "1.46.0",
+ "@playwright/test": "1.47.0",
"@sveltejs/adapter-auto": "3.2.5",
"@sveltejs/adapter-node": "5.2.3",
"@sveltejs/kit": "2.5.28",
diff --git a/web-app/playwright.config.ts b/web-app/playwright.config.ts
index ff4cb3d..436b260 100644
--- a/web-app/playwright.config.ts
+++ b/web-app/playwright.config.ts
@@ -9,15 +9,36 @@ const config: PlaywrightTestConfig = {
baseURL: "http://localhost:4173",
video: "retain-on-failure"
},
- testDir: "tests",
+ testDir: "./tests",
testMatch: /(.+\.)?(test|spec)\.ts/,
- retries: 3,
- // Firefox and Webkit are not packaged yet, see https://github.com/NixOS/nixpkgs/issues/288826
+ // https://github.com/NixOS/nixpkgs/issues/288826
projects: [
+ {
+ name: "Register users",
+ testMatch: /global-setup\.ts/,
+ teardown: "Delete users"
+ },
+ {
+ name: "Delete users",
+ testMatch: /global-teardown\.ts/
+ },
{
name: "Chromium",
- use: { ...devices["Desktop Chrome"] }
+ use: { ...devices["Desktop Chrome"] },
+ dependencies: ["Register users"]
+ },
+ {
+ name: "Firefox",
+ use: { ...devices["Desktop Firefox"] },
+ dependencies: ["Register users"]
}
+ /*
+ Upstream bug "Error: browserContext.newPage: Target page, context or browser has been closed"
+ {
+ name: "Webkit",
+ use: { ...devices["Desktop Safari"] },
+ dependencies: ["Register users"]
+ } */
]
};
diff --git a/web-app/src/routes/(anonymous)/login/+page.svelte b/web-app/src/routes/(anonymous)/login/+page.svelte
index bb9d470..3fae47c 100644
--- a/web-app/src/routes/(anonymous)/login/+page.svelte
+++ b/web-app/src/routes/(anonymous)/login/+page.svelte
@@ -25,5 +25,5 @@
-
+
diff --git a/web-app/src/routes/(anonymous)/register/+page.svelte b/web-app/src/routes/(anonymous)/register/+page.svelte
index 5939ad5..5e1262a 100644
--- a/web-app/src/routes/(anonymous)/register/+page.svelte
+++ b/web-app/src/routes/(anonymous)/register/+page.svelte
@@ -52,7 +52,7 @@
-
+
diff --git a/web-app/src/routes/(authenticated)/+page.svelte b/web-app/src/routes/(authenticated)/+page.svelte
index ec355b1..c661afb 100644
--- a/web-app/src/routes/(authenticated)/+page.svelte
+++ b/web-app/src/routes/(authenticated)/+page.svelte
@@ -39,7 +39,7 @@
-
+
@@ -72,7 +72,7 @@
>
-
+
@@ -105,7 +105,7 @@
>
-
+
{#if website.length > 0}
@@ -92,7 +92,7 @@
value={max_storage_size}
/>
-
+
diff --git a/web-app/src/routes/(authenticated)/website/[websiteId]/+page.server.ts b/web-app/src/routes/(authenticated)/website/[websiteId]/+page.server.ts
index f9d0614..a199a04 100644
--- a/web-app/src/routes/(authenticated)/website/[websiteId]/+page.server.ts
+++ b/web-app/src/routes/(authenticated)/website/[websiteId]/+page.server.ts
@@ -43,7 +43,6 @@ export const actions: Actions = {
};
if (faviconFile) {
- headers["X-Mimetype"] = faviconFile.type;
headers["X-Original-Filename"] = faviconFile.name;
}
@@ -84,7 +83,6 @@ export const actions: Actions = {
};
if (logoImage) {
- headers["X-Mimetype"] = logoImage.type;
headers["X-Original-Filename"] = logoImage.name;
}
@@ -152,7 +150,6 @@ export const actions: Actions = {
"Content-Type": "application/octet-stream",
Accept: "application/vnd.pgrst.object+json",
"X-Website-Id": params.websiteId,
- "X-Mimetype": file.type,
"X-Original-Filename": file.name
},
body: await file.arrayBuffer(),
diff --git a/web-app/src/routes/(authenticated)/website/[websiteId]/+page.svelte b/web-app/src/routes/(authenticated)/website/[websiteId]/+page.svelte
index 93bab0a..efd156d 100644
--- a/web-app/src/routes/(authenticated)/website/[websiteId]/+page.svelte
+++ b/web-app/src/routes/(authenticated)/website/[websiteId]/+page.svelte
@@ -95,7 +95,7 @@
{/if}
-
+
@@ -142,7 +142,7 @@
{/if}
-
+
@@ -165,7 +165,7 @@
content={data.home.main_content}
/>
-
+
@@ -182,7 +182,7 @@
>
-
+
diff --git a/web-app/src/routes/(authenticated)/website/[websiteId]/articles/+page.svelte b/web-app/src/routes/(authenticated)/website/[websiteId]/articles/+page.svelte
index a2ad178..7af5c50 100644
--- a/web-app/src/routes/(authenticated)/website/[websiteId]/articles/+page.svelte
+++ b/web-app/src/routes/(authenticated)/website/[websiteId]/articles/+page.svelte
@@ -40,7 +40,7 @@
-
+
@@ -74,7 +74,7 @@
>
-
+
diff --git a/web-app/src/routes/(authenticated)/website/[websiteId]/articles/[articleId]/+page.server.ts b/web-app/src/routes/(authenticated)/website/[websiteId]/articles/[articleId]/+page.server.ts
index becc990..dc906da 100644
--- a/web-app/src/routes/(authenticated)/website/[websiteId]/articles/[articleId]/+page.server.ts
+++ b/web-app/src/routes/(authenticated)/website/[websiteId]/articles/[articleId]/+page.server.ts
@@ -40,7 +40,6 @@ export const actions: Actions = {
};
if (coverFile) {
- headers["X-Mimetype"] = coverFile.type;
headers["X-Original-Filename"] = coverFile.name;
}
@@ -82,7 +81,6 @@ export const actions: Actions = {
"Content-Type": "application/octet-stream",
Accept: "application/vnd.pgrst.object+json",
"X-Website-Id": params.websiteId,
- "X-Mimetype": file.type,
"X-Original-Filename": file.name
},
body: await file.arrayBuffer(),
diff --git a/web-app/src/routes/(authenticated)/website/[websiteId]/articles/[articleId]/+page.svelte b/web-app/src/routes/(authenticated)/website/[websiteId]/articles/[articleId]/+page.svelte
index 188f6e3..0e37219 100644
--- a/web-app/src/routes/(authenticated)/website/[websiteId]/articles/[articleId]/+page.svelte
+++ b/web-app/src/routes/(authenticated)/website/[websiteId]/articles/[articleId]/+page.svelte
@@ -119,7 +119,7 @@
content={data.article.main_content ?? ""}
/>
-
+
diff --git a/web-app/src/routes/(authenticated)/website/[websiteId]/categories/+page.svelte b/web-app/src/routes/(authenticated)/website/[websiteId]/categories/+page.svelte
index 038e1d6..5b8b0ef 100644
--- a/web-app/src/routes/(authenticated)/website/[websiteId]/categories/+page.svelte
+++ b/web-app/src/routes/(authenticated)/website/[websiteId]/categories/+page.svelte
@@ -44,7 +44,7 @@
-
+
diff --git a/web-app/src/routes/(authenticated)/website/[websiteId]/collaborators/+page.svelte b/web-app/src/routes/(authenticated)/website/[websiteId]/collaborators/+page.svelte
index 6c872c8..12b95b1 100644
--- a/web-app/src/routes/(authenticated)/website/[websiteId]/collaborators/+page.svelte
+++ b/web-app/src/routes/(authenticated)/website/[websiteId]/collaborators/+page.svelte
@@ -51,7 +51,9 @@
-
+
diff --git a/web-app/src/routes/(authenticated)/website/[websiteId]/legal-information/+page.server.ts b/web-app/src/routes/(authenticated)/website/[websiteId]/legal-information/+page.server.ts
index 16828f8..02aa560 100644
--- a/web-app/src/routes/(authenticated)/website/[websiteId]/legal-information/+page.server.ts
+++ b/web-app/src/routes/(authenticated)/website/[websiteId]/legal-information/+page.server.ts
@@ -68,7 +68,6 @@ export const actions: Actions = {
"Content-Type": "application/octet-stream",
Accept: "application/vnd.pgrst.object+json",
"X-Website-Id": params.websiteId,
- "X-Mimetype": file.type,
"X-Original-Filename": file.name
},
body: await file.arrayBuffer(),
diff --git a/web-app/src/routes/(authenticated)/website/[websiteId]/legal-information/+page.svelte b/web-app/src/routes/(authenticated)/website/[websiteId]/legal-information/+page.svelte
index e775a9a..d87a7db 100644
--- a/web-app/src/routes/(authenticated)/website/[websiteId]/legal-information/+page.svelte
+++ b/web-app/src/routes/(authenticated)/website/[websiteId]/legal-information/+page.svelte
@@ -61,7 +61,9 @@
content={data.legalInformation?.main_content ?? ""}
/>
-
+
{#if data.legalInformation?.main_content}
diff --git a/web-app/src/routes/(authenticated)/website/[websiteId]/logs/+page.svelte b/web-app/src/routes/(authenticated)/website/[websiteId]/logs/+page.svelte
index bd8a6ff..9e1a3c9 100644
--- a/web-app/src/routes/(authenticated)/website/[websiteId]/logs/+page.svelte
+++ b/web-app/src/routes/(authenticated)/website/[websiteId]/logs/+page.svelte
@@ -19,13 +19,13 @@
if (data.website.content_type === "Blog") {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
- const { user, change_log, media, docs_category, ...restTables } = tables;
+ const { user, change_log, docs_category, ...restTables } = tables;
resources = restTables;
}
if (data.website.content_type === "Docs") {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
- const { user, change_log, media, ...restTables } = tables;
+ const { user, change_log, ...restTables } = tables;
resources = restTables;
}
@@ -96,7 +96,7 @@
-
+
diff --git a/web-app/src/routes/(authenticated)/website/[websiteId]/publish/+page.svelte b/web-app/src/routes/(authenticated)/website/[websiteId]/publish/+page.svelte
index 1409fa2..27f545f 100644
--- a/web-app/src/routes/(authenticated)/website/[websiteId]/publish/+page.svelte
+++ b/web-app/src/routes/(authenticated)/website/[websiteId]/publish/+page.svelte
@@ -36,7 +36,9 @@
be published on the Internet.
@@ -73,7 +75,9 @@
required
/>
-
+
{#if data.websiteOverview.domain_prefix?.prefix}
diff --git a/web-app/template-styles/common-styles.css b/web-app/template-styles/common-styles.css
index c7b1b66..ea4eeca 100644
--- a/web-app/template-styles/common-styles.css
+++ b/web-app/template-styles/common-styles.css
@@ -32,6 +32,7 @@ input,
button,
textarea,
select,
+input[type="file"]::file-selector-button,
a[role="button"],
label[for="toggle-mobile-preview"],
label[for="toggle-sidebar"],
@@ -59,6 +60,11 @@ input[type="file"] {
inline-size: 100%;
}
+input[type="file"]::file-selector-button {
+ padding-block: calc(var(--space-3xs) / 4);
+ margin-inline-end: var(--space-2xs);
+}
+
input[type="color"] {
padding: 0;
}
@@ -73,6 +79,7 @@ summary {
}
button,
+input[type="file"]::file-selector-button,
a[role="button"],
label[for="toggle-mobile-preview"],
label[for="toggle-sidebar"],
@@ -226,12 +233,14 @@ pre {
flex-shrink: 0;
}
-code {
+code,
+kbd {
font-family: monospace;
font-size: var(--font-size--1);
}
-:not(pre) > code {
+:not(pre) > code,
+kbd {
background-color: var(--bg-secondary);
border: var(--border-primary);
padding-inline: var(--space-3xs);
@@ -278,6 +287,16 @@ del {
color: var(--color-text-invert);
}
+blockquote {
+ border-inline-start: var(--border-primary);
+ border-width: 0.25rem;
+ padding-inline-start: var(--space-xs);
+}
+
+meter {
+ inline-size: min(512px, 100%);
+}
+
.hljs {
color: var(--hl-color);
background: var(--hl-bg);
diff --git a/web-app/tests/account.spec.ts b/web-app/tests/account.spec.ts
index 5788343..15d1c6c 100644
--- a/web-app/tests/account.spec.ts
+++ b/web-app/tests/account.spec.ts
@@ -1,55 +1,25 @@
-import { test as base, expect, type Page } from "@playwright/test";
-import { randomBytes } from "node:crypto";
+import { test, expect } from "@playwright/test";
+import { userOwner, register, authenticate, password } from "./shared";
-const username = randomBytes(8).toString("hex");
-const password = "T3stuser??!!";
+const userDeleted = "test-deleted-a";
-const test = base.extend<{ authenticatedPage: Page }>({
- authenticatedPage: async ({ page }, use) => {
- await page.goto("/login");
- await page.getByLabel("Username:").fill(username);
- await page.getByLabel("Password:").fill(password);
- await page.getByRole("button", { name: "Submit" }).click();
- await use(page);
- }
+test(`Logout`, async ({ page }) => {
+ await authenticate(userOwner, page);
+ await page.getByRole("link", { name: "Account" }).click();
+ await page.getByRole("button", { name: "Logout" }).click();
+ await expect(page.getByRole("heading", { name: "Login" })).toBeVisible();
});
-test.describe.serial("Account tests", () => {
- test("Register", async ({ page }) => {
- await page.goto("/register");
- await page.getByLabel("Username:").click();
- await page.getByLabel("Username:").fill(username);
- await page.getByLabel("Password:").click();
- await page.getByLabel("Password:").fill(password);
- await page.getByRole("button", { name: "Submit" }).click();
- await expect(page.getByText("Successfully registered, you")).toBeVisible();
- });
-
- test("Login", async ({ page }) => {
- await page.goto("/login");
- await page.getByLabel("Username:").click();
- await page.getByLabel("Username:").fill(username);
- await page.getByLabel("Password:").click();
- await page.getByLabel("Password:").fill(password);
- await page.getByRole("button", { name: "Submit" }).click();
- await expect(page.getByRole("heading", { name: "Dashboard" })).toBeVisible();
- });
-
- test("Logout", async ({ authenticatedPage: page }) => {
- await page.getByRole("link", { name: "Account" }).click();
- await page.getByRole("button", { name: "Logout" }).click();
- await expect(page.getByRole("heading", { name: "Login" })).toBeVisible();
- });
-
- test("Delete account", async ({ authenticatedPage: page }) => {
- await page.getByRole("link", { name: "Account" }).click();
- await page.getByRole("button", { name: "Delete account" }).click();
- await page.getByLabel("Password:").click();
- await page.getByLabel("Password:").fill(password);
- await page
- .locator("#delete-account-modal")
- .getByRole("button", { name: "Delete account" })
- .click();
- await expect(page.getByRole("heading", { name: "Login" })).toBeVisible();
- });
+test(`Delete account`, async ({ page }) => {
+ await register(userDeleted, page);
+ await authenticate(userDeleted, page);
+ await page.getByRole("link", { name: "Account" }).click();
+ await page.getByRole("button", { name: "Delete account" }).click();
+ await page.getByLabel("Password:").click();
+ await page.getByLabel("Password:").fill(password);
+ await page
+ .locator("#delete-account-modal")
+ .getByRole("button", { name: "Delete account" })
+ .click();
+ await expect(page.getByRole("heading", { name: "Login" })).toBeVisible();
});
diff --git a/web-app/tests/articles.spec.ts b/web-app/tests/articles.spec.ts
new file mode 100644
index 0000000..c5989eb
--- /dev/null
+++ b/web-app/tests/articles.spec.ts
@@ -0,0 +1,151 @@
+import { test, expect } from "@playwright/test";
+import {
+ userOwner,
+ authenticate,
+ permissionLevels,
+ collabUsers,
+ collabTestingWebsite
+} from "./shared";
+
+test.describe("Website owner", () => {
+ test.beforeEach(async ({ page }) => {
+ await authenticate(userOwner, page);
+ await page
+ .locator("li")
+ .filter({ hasText: collabTestingWebsite })
+ .getByRole("link", { name: collabTestingWebsite })
+ .click();
+ await page.getByRole("link", { name: "Articles" }).click();
+ });
+
+ test(`Create article`, async ({ page }) => {
+ await page.getByRole("button", { name: "Create article" }).click();
+ await page.locator("#create-article-modal").getByLabel("Title:").click();
+ await page.locator("#create-article-modal").getByLabel("Title:").fill("Article");
+ await page
+ .locator("#create-article-modal")
+ .getByRole("button", { name: "Create article" })
+ .click();
+ await expect(page.getByText("Successfully created article")).toBeVisible();
+ await expect(page.getByRole("link", { name: "All articles" })).toBeVisible();
+ });
+
+ test.describe("Modify", () => {
+ test.beforeEach(async ({ page }) => {
+ await page.getByRole("button", { name: "Create article" }).click();
+ await page.locator("#create-article-modal").getByLabel("Title:").click();
+ await page.locator("#create-article-modal").getByLabel("Title:").fill("Article");
+ await page
+ .locator("#create-article-modal")
+ .getByRole("button", { name: "Create article" })
+ .click();
+ });
+
+ test(`Update article`, async ({ page }) => {
+ await page.getByRole("link", { name: "Edit" }).first().click();
+ await page.getByLabel("Weight:").click();
+ await page.getByLabel("Weight:").fill("555");
+ await page.getByLabel("Title:").click();
+ await page.getByLabel("Title:").press("ControlOrMeta+a");
+ await page.getByLabel("Title:").fill("Example article");
+ await page.getByLabel("Description:").click();
+ await page.getByLabel("Description:").fill("Random description");
+ await page.getByLabel("Author:").click();
+ await page.getByLabel("Author:").fill("John Doe");
+ await page.getByLabel("Main content:").click();
+ await page.getByLabel("Main content:").fill("## Markdown content comes here");
+ await page.getByRole("button", { name: "Update article" }).click();
+ await expect(page.getByText("Successfully updated article")).toBeVisible();
+ });
+
+ test(`Delete article`, async ({ page }) => {
+ await page.getByRole("button", { name: "Delete" }).first().click();
+ await page.getByRole("button", { name: "Delete article" }).click();
+ await expect(page.getByText("Successfully deleted article")).toBeVisible();
+ });
+ });
+});
+
+for (const permissionLevel of permissionLevels) {
+ test.describe(`Website collaborator (Permission level: ${permissionLevel})`, () => {
+ test.beforeEach(async ({ page }) => {
+ await authenticate(collabUsers.get(permissionLevel)!, page);
+ await page
+ .locator("li")
+ .filter({ hasText: collabTestingWebsite })
+ .getByRole("link", { name: collabTestingWebsite })
+ .click();
+ await page.getByRole("link", { name: "Articles" }).click();
+ });
+
+ test(`Create article`, async ({ page }) => {
+ await page.getByRole("button", { name: "Create article" }).click();
+ await page.locator("#create-article-modal").getByLabel("Title:").click();
+ await page.locator("#create-article-modal").getByLabel("Title:").fill("Article");
+ await page
+ .locator("#create-article-modal")
+ .getByRole("button", { name: "Create article" })
+ .evaluate((node) => node.removeAttribute("disabled"));
+ await page
+ .locator("#create-article-modal")
+ .getByRole("button", { name: "Create article" })
+ .click();
+
+ if (permissionLevel === 10) {
+ await expect(page.getByText("Insufficient permissions")).toBeVisible();
+ } else {
+ await expect(page.getByText("Successfully created article")).toBeVisible();
+ await expect(page.getByRole("link", { name: "All articles" })).toBeVisible();
+ }
+ });
+
+ test(`Update article`, async ({ page }) => {
+ await page.getByRole("link", { name: "Edit" }).first().click();
+ await page.getByLabel("Weight:").click();
+ await page.getByLabel("Weight:").fill("555");
+ await page.getByLabel("Title:").click();
+ await page.getByLabel("Title:").press("ControlOrMeta+a");
+ await page.getByLabel("Title:").fill("Example article");
+ await page.getByLabel("Description:").click();
+ await page.getByLabel("Description:").fill("Random description");
+ await page.getByLabel("Author:").click();
+ await page.getByLabel("Author:").fill("John Doe");
+ await page.getByLabel("Main content:").click();
+ await page.getByLabel("Main content:").fill("## Markdown content comes here");
+ await page
+ .getByRole("button", { name: "Update article" })
+ .evaluate((node) => node.removeAttribute("disabled"));
+ await page.getByRole("button", { name: "Update article" }).click();
+
+ if (permissionLevel === 10) {
+ await expect(page.getByText("Insufficient permissions")).toBeVisible();
+ } else {
+ await expect(page.getByText("Successfully updated article")).toBeVisible();
+ }
+ });
+
+ test(`Delete article`, async ({ page }) => {
+ await page.getByRole("button", { name: "Delete" }).first().click();
+ await page
+ .getByRole("button", { name: "Delete article" })
+ .evaluate((node) => node.removeAttribute("disabled"));
+ await page.getByRole("button", { name: "Delete article" }).click();
+
+ switch (permissionLevel) {
+ case 10:
+ await expect(page.getByText("Insufficient permissions")).toBeVisible();
+ break;
+ case 20:
+ await expect(
+ page
+ .getByText("Successfully deleted article")
+ .or(page.getByText("Insufficient permissions"))
+ ).toBeVisible();
+ break;
+ case 30:
+ await expect(page.getByText("Successfully deleted article")).toBeVisible();
+ break;
+ }
+ });
+ });
+}
diff --git a/web-app/tests/categories.spec.ts b/web-app/tests/categories.spec.ts
new file mode 100644
index 0000000..60886e0
--- /dev/null
+++ b/web-app/tests/categories.spec.ts
@@ -0,0 +1,149 @@
+import { test, expect } from "@playwright/test";
+import { randomBytes, randomInt } from "node:crypto";
+import {
+ userOwner,
+ authenticate,
+ permissionLevels,
+ collabUsers,
+ collabTestingWebsite
+} from "./shared";
+
+const genCategoryName = () => randomBytes(12).toString("hex");
+const genCategoryWeight = (min = 10, max = 10000) => randomInt(min, max).toString();
+
+test.describe("Website owner", () => {
+ test.beforeEach(async ({ page }) => {
+ await authenticate(userOwner, page);
+ await page
+ .locator("li")
+ .filter({ hasText: collabTestingWebsite })
+ .getByRole("link", { name: collabTestingWebsite })
+ .click();
+ await page.getByRole("link", { name: "Categories" }).click();
+ });
+
+ test(`Create category`, async ({ page }) => {
+ await page.getByRole("button", { name: "Create category" }).click();
+ await page.locator("#create-category-modal").getByLabel("Name:").click();
+ await page.locator("#create-category-modal").getByLabel("Name:").fill(genCategoryName());
+ await page.locator("#create-category-modal").getByLabel("Weight:").click();
+ await page.locator("#create-category-modal").getByLabel("Weight:").fill(genCategoryWeight());
+ await page
+ .locator("#create-category-modal")
+ .getByRole("button", { name: "Create category" })
+ .click();
+ await expect(page.getByText("Successfully created category")).toBeVisible();
+ await expect(page.getByRole("link", { name: "All categories" })).toBeVisible();
+ });
+
+ test.describe("Modify", () => {
+ test.beforeEach(async ({ page }) => {
+ await page.getByRole("button", { name: "Create category" }).click();
+ await page.locator("#create-category-modal").getByLabel("Name:").click();
+ await page.locator("#create-category-modal").getByLabel("Name:").fill(genCategoryName());
+ await page.locator("#create-category-modal").getByLabel("Weight:").click();
+ await page.locator("#create-category-modal").getByLabel("Weight:").fill(genCategoryWeight());
+ await page
+ .locator("#create-category-modal")
+ .getByRole("button", { name: "Create category" })
+ .click();
+ });
+
+ test(`Update category`, async ({ page }) => {
+ await page.getByRole("button", { name: "Update" }).first().click();
+ const modalName = page.url().split("#")[1];
+ await page.locator(`#${modalName}`).getByLabel("Name:").click();
+ await page.locator(`#${modalName}`).getByLabel("Name:").fill(genCategoryName());
+ await page.locator(`#${modalName}`).getByLabel("Weight:").click();
+ await page.locator(`#${modalName}`).getByLabel("Weight:").fill(genCategoryWeight());
+ await page.getByRole("button", { name: "Update category" }).click();
+ await expect(page.getByText("Successfully updated category")).toBeVisible();
+ });
+
+ test(`Delete category`, async ({ page }) => {
+ await page.getByRole("button", { name: "Delete" }).first().click();
+ await page.getByRole("button", { name: "Delete category" }).click();
+ await expect(page.getByText("Successfully deleted category")).toBeVisible();
+ });
+ });
+});
+
+for (const permissionLevel of permissionLevels) {
+ test.describe(`Website collaborator (Permission level: ${permissionLevel})`, () => {
+ test.beforeEach(async ({ page }) => {
+ await authenticate(collabUsers.get(permissionLevel)!, page);
+ await page
+ .locator("li")
+ .filter({ hasText: collabTestingWebsite })
+ .getByRole("link", { name: collabTestingWebsite })
+ .click();
+ await page.getByRole("link", { name: "Categories" }).click();
+ });
+
+ test(`Create category`, async ({ page }) => {
+ await page.getByRole("button", { name: "Create category" }).click();
+ await page.locator("#create-category-modal").getByLabel("Name:").click();
+ await page.locator("#create-category-modal").getByLabel("Name:").fill(genCategoryName());
+ await page.locator("#create-category-modal").getByLabel("Weight:").click();
+ await page.locator("#create-category-modal").getByLabel("Weight:").fill(genCategoryWeight());
+ await page
+ .locator("#create-category-modal")
+ .getByRole("button", { name: "Create category" })
+ .evaluate((node) => node.removeAttribute("disabled"));
+ await page
+ .locator("#create-category-modal")
+ .getByRole("button", { name: "Create category" })
+ .click();
+
+ if (permissionLevel === 10) {
+ await expect(page.getByText("Insufficient permissions")).toBeVisible();
+ } else {
+ await expect(page.getByText("Successfully created category")).toBeVisible();
+ await expect(page.getByRole("link", { name: "All categories" })).toBeVisible();
+ }
+ });
+
+ test(`Update category`, async ({ page }) => {
+ await page.getByRole("button", { name: "Update" }).first().click();
+ const modalName = page.url().split("#")[1];
+ await page.locator(`#${modalName}`).getByLabel("Name:").click();
+ await page.locator(`#${modalName}`).getByLabel("Name:").fill(genCategoryName());
+ await page.locator(`#${modalName}`).getByLabel("Weight:").click();
+ await page.locator(`#${modalName}`).getByLabel("Weight:").fill(genCategoryWeight());
+ await page
+ .getByRole("button", { name: "Update category" })
+ .evaluate((node) => node.removeAttribute("disabled"));
+ await page.getByRole("button", { name: "Update category" }).click();
+
+ if (permissionLevel === 10) {
+ await expect(page.getByText("Insufficient permissions")).toBeVisible();
+ } else {
+ await expect(page.getByText("Successfully updated category")).toBeVisible();
+ }
+ });
+
+ test(`Delete category`, async ({ page }) => {
+ await page.getByRole("button", { name: "Delete" }).first().click();
+ await page
+ .getByRole("button", { name: "Delete category" })
+ .evaluate((node) => node.removeAttribute("disabled"));
+ await page.getByRole("button", { name: "Delete category" }).click();
+
+ switch (permissionLevel) {
+ case 10:
+ await expect(page.getByText("Insufficient permissions")).toBeVisible();
+ break;
+ case 20:
+ await expect(
+ page
+ .getByText("Successfully deleted category")
+ .or(page.getByText("Insufficient permissions"))
+ ).toBeVisible();
+ break;
+ case 30:
+ await expect(page.getByText("Successfully deleted category")).toBeVisible();
+ break;
+ }
+ });
+ });
+}
diff --git a/web-app/tests/collaborator.spec.ts b/web-app/tests/collaborator.spec.ts
deleted file mode 100644
index fe75950..0000000
--- a/web-app/tests/collaborator.spec.ts
+++ /dev/null
@@ -1,622 +0,0 @@
-import { test, expect } from "@playwright/test";
-import { randomBytes } from "node:crypto";
-import { platform } from "node:os";
-
-const username = randomBytes(8).toString("hex");
-const collabUsername = randomBytes(8).toString("hex");
-const collabUsername2 = randomBytes(8).toString("hex");
-const collabUsername3 = randomBytes(8).toString("hex");
-const collabUsername4 = randomBytes(8).toString("hex");
-const customPrefix = Buffer.from(randomBytes(16).map((byte) => (byte % 26) + 97)).toString();
-const customPrefix2 = Buffer.from(randomBytes(16).map((byte) => (byte % 26) + 97)).toString();
-const password = "T3stuser??!!";
-
-const permissionLevels = [10, 20, 30];
-
-test.describe.serial("Collaborator tests", () => {
- test("Setup", async ({ page }) => {
- await page.goto("/register");
-
- await page.getByLabel("Username:").click();
- await page.getByLabel("Username:").fill(username);
- await page.getByLabel("Password:").click();
- await page.getByLabel("Password:").fill(password);
- await page.getByRole("button", { name: "Submit" }).click();
-
- await page.getByLabel("Username:").click();
- await page.getByLabel("Username:").fill(collabUsername);
- await page.getByLabel("Password:").click();
- await page.getByLabel("Password:").fill(password);
- await page.getByRole("button", { name: "Submit" }).click();
-
- await page.getByLabel("Username:").click();
- await page.getByLabel("Username:").fill(collabUsername2);
- await page.getByLabel("Password:").click();
- await page.getByLabel("Password:").fill(password);
- await page.getByRole("button", { name: "Submit" }).click();
-
- await page.getByLabel("Username:").click();
- await page.getByLabel("Username:").fill(collabUsername3);
- await page.getByLabel("Password:").click();
- await page.getByLabel("Password:").fill(password);
- await page.getByRole("button", { name: "Submit" }).click();
-
- await page.getByLabel("Username:").click();
- await page.getByLabel("Username:").fill(collabUsername4);
- await page.getByLabel("Password:").click();
- await page.getByLabel("Password:").fill(password);
- await page.getByRole("button", { name: "Submit" }).click();
-
- await page.goto("/login");
- await page.getByLabel("Username:").fill(username);
- await page.getByLabel("Password:").fill(password);
- await page.getByRole("button", { name: "Submit" }).click();
-
- await page.getByRole("button", { name: "Create website" }).click();
- await page.getByLabel("Title:").click();
- await page.getByLabel("Title:").fill("Blog");
- await page.getByRole("button", { name: "Submit" }).click();
- await page.getByRole("link", { name: "Blog" }).click();
- await page.getByRole("link", { name: "Publish" }).click();
- await page.getByRole("button", { name: "Publish" }).click();
- await page.getByLabel("Prefix:").click();
- await page.getByLabel("Prefix:").fill(customPrefix);
- await page.getByRole("button", { name: "Submit" }).click();
-
- await page.goto("/");
- await page.getByRole("button", { name: "Create website" }).click();
- await page.getByLabel("Type: BlogDocs").selectOption("Docs");
- await page.getByLabel("Title:").click();
- await page.getByLabel("Title:").fill("Documentation");
- await page.getByRole("button", { name: "Submit" }).click();
-
- await page.getByRole("link", { name: "Blog" }).click();
- await page.getByRole("link", { name: "Articles" }).click();
- await page.getByRole("button", { name: "Create article" }).click();
- await page.getByLabel("Title:").click();
- await page.getByLabel("Title:").fill("Article-10");
- await page.getByRole("button", { name: "Submit" }).click();
-
- await page.getByRole("link", { name: "Collaborators" }).click();
- await page.getByRole("button", { name: "Add collaborator" }).click();
- await page.getByLabel("Username:").click();
- await page.getByLabel("Username:").fill(collabUsername);
- await page.getByRole("button", { name: "Submit" }).click();
- await page.getByRole("button", { name: "Add collaborator" }).click();
- await page.getByLabel("Username:").click();
- await page.getByLabel("Username:").fill(collabUsername2);
- await page.getByRole("button", { name: "Submit" }).click();
- await page.getByRole("button", { name: "Add collaborator" }).click();
- await page.getByLabel("Username:").click();
- await page.getByLabel("Username:").fill(collabUsername3);
- await page.getByRole("combobox").selectOption("30");
- await page.getByRole("button", { name: "Submit" }).click();
-
- await page.goto("/");
- await page.getByRole("link", { name: "Documentation" }).click();
- await page.getByRole("link", { name: "Categories" }).click();
- await page.getByRole("button", { name: "Create category" }).click();
- await page.getByLabel("Name:").nth(0).click();
- await page.getByLabel("Name:").nth(0).fill("Category-10");
- await page.getByLabel("Weight:").click();
- await page.getByLabel("Weight:").fill("10");
- await page.getByRole("button", { name: "Submit" }).click();
-
- await page.getByRole("link", { name: "Collaborators" }).click();
- await page.getByRole("button", { name: "Add collaborator" }).click();
- await page.getByLabel("Username:").click();
- await page.getByLabel("Username:").fill(collabUsername);
- await page.getByRole("button", { name: "Submit" }).click();
-
- await page.getByRole("link", { name: "Legal information" }).click();
- await page.getByLabel("Main content:").click();
- await page.getByLabel("Main content:").fill("## Content");
- await page.getByRole("button", { name: "Submit" }).click();
- });
-
- for (const permissionLevel of permissionLevels) {
- test(`Set collaborator permission level to ${permissionLevel}`, async ({ page }) => {
- await page.goto("/login");
- await page.getByLabel("Username:").fill(username);
- await page.getByLabel("Password:").fill(password);
- await page.getByRole("button", { name: "Submit" }).click();
- await page.getByRole("link", { name: "Blog" }).click();
- await page.getByRole("link", { name: "Collaborators" }).click();
- await page
- .locator("li")
- .filter({ hasText: collabUsername })
- .getByRole("button")
- .first()
- .click();
- await page.getByRole("combobox").selectOption(permissionLevel.toString());
- await page.getByRole("button", { name: "Update collaborator" }).click();
-
- await page.goto("/");
- await page.getByRole("link", { name: "Documentation" }).click();
- await page.getByRole("link", { name: "Collaborators" }).click();
- await page
- .locator("li")
- .filter({ hasText: collabUsername })
- .getByRole("button")
- .first()
- .click();
- await page.getByRole("combobox").selectOption(permissionLevel.toString());
- await page.getByRole("button", { name: "Update collaborator" }).click();
- });
-
- test.describe.serial(`Permission level: ${permissionLevel}`, () => {
- test.beforeEach(async ({ page }) => {
- await page.goto("/login");
- await page.getByLabel("Username:").fill(collabUsername);
- await page.getByLabel("Password:").fill(password);
- await page.getByRole("button", { name: "Submit" }).click();
- });
-
- test("Update website", async ({ page }) => {
- await page.locator("li").filter({ hasText: "Blog" }).getByRole("button").first().click();
- const modalName = page.url().split("#")[1];
- await page
- .locator(`div[id="${modalName}"] button[type="submit"]`)
- .evaluate((node) => node.removeAttribute("disabled"));
- await page.getByRole("button", { name: "Submit" }).click();
-
- if ([10, 20].includes(permissionLevel)) {
- await expect(page.getByText("Insufficient permissions")).toBeVisible();
- } else {
- await expect(page.getByText("Successfully updated website")).toBeVisible();
- }
- });
- test("Delete website", async ({ page }) => {
- await page.locator("li").filter({ hasText: "Blog" }).getByRole("button").nth(1).click();
- const modalName = page.url().split("#")[1];
- await page
- .locator(`div[id="${modalName}"] button[type="submit"]`)
- .evaluate((node) => node.removeAttribute("disabled"));
- await page.getByRole("button", { name: "Delete website" }).click();
- await expect(page.getByText("Insufficient permissions")).toBeVisible();
- });
- test("Update Global", async ({ page }) => {
- await page.getByRole("link", { name: "Blog" }).click();
- await page
- .locator('#global button[type="submit"]')
- .evaluate((node) => node.removeAttribute("disabled"));
- await page.locator("#global").getByRole("button", { name: "Submit" }).click();
-
- if (permissionLevel === 10) {
- await expect(page.getByText("Insufficient permissions")).toBeVisible();
- } else {
- await expect(page.getByText("Successfully updated global")).toBeVisible();
- }
- });
- test("Update Header", async ({ page }) => {
- await page.getByRole("link", { name: "Blog" }).click();
- await page
- .locator('#header button[type="submit"]')
- .evaluate((node) => node.removeAttribute("disabled"));
- await page.locator("#header").getByRole("button", { name: "Submit" }).click();
-
- if (permissionLevel === 10) {
- await expect(page.getByText("Insufficient permissions")).toBeVisible();
- } else {
- await expect(page.getByText("Successfully updated header")).toBeVisible();
- }
- });
- test("Update Home", async ({ page }) => {
- await page.getByRole("link", { name: "Blog" }).click();
- await page.getByLabel("Description:").click();
- await page.getByLabel("Description:").fill("Description");
- await page
- .locator('#home button[type="submit"]')
- .evaluate((node) => node.removeAttribute("disabled"));
- await page.locator("#home").getByRole("button", { name: "Submit" }).click();
-
- if (permissionLevel === 10) {
- await expect(page.getByText("Insufficient permissions")).toBeVisible();
- } else {
- await expect(page.getByText("Successfully updated home")).toBeVisible();
- }
- });
- test("Update Footer", async ({ page }) => {
- await page.getByRole("link", { name: "Blog" }).click();
- await page
- .locator('#footer button[type="submit"]')
- .evaluate((node) => node.removeAttribute("disabled"));
- await page.locator("#footer").getByRole("button", { name: "Submit" }).click();
-
- if (permissionLevel === 10) {
- await expect(page.getByText("Insufficient permissions")).toBeVisible();
- } else {
- await expect(page.getByText("Successfully updated footer")).toBeVisible();
- }
- });
- test("Create article", async ({ page }) => {
- await page.getByRole("link", { name: "Blog" }).click();
- await page.getByRole("link", { name: "Articles" }).click();
- await page.getByRole("button", { name: "Create article" }).click();
- await page.getByLabel("Title:").click();
- await page.getByLabel("Title:").fill(`Article-${permissionLevel}`);
- await page
- .locator('form[action="?/createArticle"] button[type="submit"]')
- .evaluate((node) => node.removeAttribute("disabled"));
- await page.getByRole("button", { name: "Submit" }).click();
-
- if (permissionLevel === 10) {
- await expect(page.getByText("Insufficient permissions")).toBeVisible();
- } else {
- await expect(page.getByText("Successfully created article")).toBeVisible();
- }
- });
- test("Update article", async ({ page }) => {
- await page.getByRole("link", { name: "Blog" }).click();
- await page.getByRole("link", { name: "Articles" }).click();
- await page
- .locator("li")
- .filter({ hasText: `Article-${permissionLevel}` })
- .getByRole("link")
- .click();
- await page.getByLabel("Description:").click();
- await page.getByLabel("Description:").fill("Description");
- await page.getByLabel("Author:").click();
- await page.getByLabel("Author:").fill("Author");
- await page.getByLabel("Main content:").click();
- await page.getByLabel("Main content:").fill("## Main content");
- await page
- .locator('form button[type="submit"]')
- .evaluate((node) => node.removeAttribute("disabled"));
- await page.getByRole("button", { name: "Submit" }).click();
-
- if (permissionLevel === 10) {
- await expect(page.getByText("Insufficient permissions")).toBeVisible();
- } else {
- await expect(page.getByText("Successfully updated article")).toBeVisible();
- }
- });
- test("Delete article", async ({ page }) => {
- await page.getByRole("link", { name: "Blog" }).click();
- await page.getByRole("link", { name: "Articles" }).click();
- await page
- .locator("li")
- .filter({ hasText: `Article-${permissionLevel}` })
- .getByRole("button")
- .click();
- const modalName = page.url().split("#")[1];
- await page
- .locator(`div[id="${modalName}"] button[type="submit"]`)
- .evaluate((node) => node.removeAttribute("disabled"));
- await page.getByRole("button", { name: "Delete article" }).click();
-
- if (permissionLevel === 10) {
- await expect(page.getByText("Insufficient permissions")).toBeVisible();
- }
- if ([20, 30].includes(permissionLevel)) {
- await expect(page.getByText("Successfully deleted article")).toBeVisible();
-
- await page.locator("li").filter({ hasText: `Article-10` }).getByRole("button").click();
- const modalName = page.url().split("#")[1];
- await page
- .locator(`div[id="${modalName}"] button[type="submit"]`)
- .evaluate((node) => node.removeAttribute("disabled"));
- await page.getByRole("button", { name: "Delete article" }).click();
-
- if (permissionLevel === 20) {
- await expect(page.getByText("Insufficient permissions")).toBeVisible();
- } else {
- await expect(page.getByText("Successfully deleted article")).toBeVisible();
- }
- }
- });
- test("Add collaborator", async ({ page }) => {
- await page.getByRole("link", { name: "Blog" }).click();
- await page.getByRole("link", { name: "Collaborators" }).click();
- await page.getByRole("button", { name: "Add collaborator" }).click();
- await page.getByLabel("Username:").click();
- await page.getByLabel("Username:").fill(collabUsername4);
- await page
- .locator('form[action="?/addCollaborator"] button[type="submit"]')
- .evaluate((node) => node.removeAttribute("disabled"));
- await page.getByRole("button", { name: "Submit" }).click();
-
- if ([10, 20].includes(permissionLevel)) {
- await expect(page.getByText("Insufficient permissions")).toBeVisible();
- } else {
- await expect(page.getByText("Successfully added")).toBeVisible();
- }
- });
- test("Update collaborator", async ({ page }) => {
- await page.getByRole("link", { name: "Blog" }).click();
- await page.getByRole("link", { name: "Collaborators" }).click();
- await page
- .locator("li")
- .filter({ hasText: collabUsername2 })
- .getByRole("button")
- .first()
- .click();
- await page.getByRole("combobox").selectOption("20");
- const modalName = page.url().split("#")[1];
- await page
- .locator(`div[id="${modalName}"] button[type="submit"]`)
- .evaluate((node) => node.removeAttribute("disabled"));
- await page.getByRole("button", { name: "Update collaborator" }).click();
-
- if ([10, 20].includes(permissionLevel)) {
- await expect(page.getByText("Insufficient permissions")).toBeVisible();
- } else {
- await expect(page.getByText("Successfully updated")).toBeVisible();
-
- await page
- .locator("li")
- .filter({ hasText: collabUsername2 })
- .getByRole("button")
- .first()
- .click();
- await page.getByRole("combobox").selectOption("30");
- await page.getByRole("button", { name: "Update collaborator" }).click();
- await expect(page.getByText("Insufficient permissions")).toBeVisible();
- }
- });
- test("Remove collaborator", async ({ page }) => {
- await page.getByRole("link", { name: "Blog" }).click();
- await page.getByRole("link", { name: "Collaborators" }).click();
- await page
- .locator("li")
- .filter({ hasText: collabUsername2 })
- .getByRole("button")
- .nth(1)
- .click();
- const modalName = page.url().split("#")[1];
- await page
- .locator(`div[id="${modalName}"] button[type="submit"]`)
- .evaluate((node) => node.removeAttribute("disabled"));
- await page.getByRole("button", { name: "Remove collaborator" }).click();
-
- if ([10, 20].includes(permissionLevel)) {
- await expect(page.getByText("Insufficient permissions")).toBeVisible();
- } else {
- await expect(page.getByText("Successfully removed")).toBeVisible();
-
- await page
- .locator("li")
- .filter({ hasText: collabUsername3 })
- .getByRole("button")
- .nth(1)
- .click();
- await page.getByRole("button", { name: "Remove collaborator" }).click();
- await expect(page.getByText("Insufficient permissions")).toBeVisible();
- }
- });
- test("Create/Update legal information", async ({ page }) => {
- await page.getByRole("link", { name: "Blog" }).click();
- await page.getByRole("link", { name: "Legal information" }).click();
- await page.getByLabel("Main content:").click();
- await page.getByLabel("Main content:").fill("## Content");
- await page
- .locator('form[action="?/createUpdateLegalInformation"] button[type="submit"]')
- .evaluate((node) => node.removeAttribute("disabled"));
- await page.getByRole("button", { name: "Submit" }).click();
-
- if (permissionLevel === 30) {
- await expect(page.getByText("Successfully created/updated legal")).toBeVisible();
- } else {
- await expect(page.getByText("Insufficient permissions")).toBeVisible();
- }
- });
- test("Delete legal information", async ({ page }) => {
- await page
- .getByRole("link", {
- name: [10, 20].includes(permissionLevel) ? "Documentation" : "Blog"
- })
- .click();
- await page.getByRole("link", { name: "Legal information" }).click();
- await page.getByRole("button", { name: "Delete" }).click();
- await page
- .locator('form[action="?/deleteLegalInformation"] button[type="submit"]')
- .evaluate((node) => node.removeAttribute("disabled"));
- await page.getByRole("button", { name: "Delete legal information" }).click();
-
- if (permissionLevel === 30) {
- await expect(page.getByText("Successfully deleted legal")).toBeVisible();
- } else {
- await expect(page.getByText("Insufficient permissions")).toBeVisible();
- }
- });
- test("Create category", async ({ page }) => {
- await page.getByRole("link", { name: "Documentation" }).click();
- await page.getByRole("link", { name: "Categories" }).click();
- await page.getByRole("button", { name: "Create category" }).click();
- await page.getByLabel("Name:").nth(0).click();
- await page.getByLabel("Name:").nth(0).fill(`Category-${permissionLevel}`);
- await page.getByRole("spinbutton", { name: "Weight:" }).click();
- await page.getByRole("spinbutton", { name: "Weight:" }).fill(permissionLevel.toString());
- await page
- .locator('form[action="?/createCategory"] button[type="submit"]')
- .evaluate((node) => node.removeAttribute("disabled"));
- await page.getByRole("button", { name: "Submit" }).click();
-
- if (permissionLevel === 10) {
- await expect(page.getByText("Insufficient permissions")).toBeVisible();
- } else {
- await expect(page.getByText("Successfully created category")).toBeVisible();
- }
- });
- test("Update category", async ({ page }) => {
- await page.getByRole("link", { name: "Documentation" }).click();
- await page.getByRole("link", { name: "Categories" }).click();
- await page
- .locator("li")
- .filter({ hasText: `Category-${permissionLevel}` })
- .getByRole("button")
- .first()
- .click();
- await page.getByRole("spinbutton", { name: "Weight:" }).click();
- await page
- .getByRole("spinbutton", { name: "Weight:" })
- .fill((permissionLevel * 2).toString());
- const modalName = page.url().split("#")[1];
- await page
- .locator(`div[id="${modalName}"] button[type="submit"]`)
- .evaluate((node) => node.removeAttribute("disabled"));
- await page.getByRole("button", { name: "Update category" }).click();
-
- if (permissionLevel === 10) {
- await expect(page.getByText("Insufficient permissions")).toBeVisible();
- } else {
- await expect(page.getByText("Successfully updated category")).toBeVisible();
- }
- });
- test("Delete category", async ({ page }) => {
- await page.getByRole("link", { name: "Documentation" }).click();
- await page.getByRole("link", { name: "Categories" }).click();
- await page
- .locator("li")
- .filter({ hasText: `Category-${permissionLevel}` })
- .getByRole("button")
- .nth(1)
- .click();
- const modalName = page.url().split("#")[1];
- await page
- .locator(`div[id="${modalName}"] button[type="submit"]`)
- .evaluate((node) => node.removeAttribute("disabled"));
- await page.getByRole("button", { name: "Delete category" }).click();
-
- if (permissionLevel === 10) {
- await expect(page.getByText("Insufficient permissions")).toBeVisible();
- }
- if ([20, 30].includes(permissionLevel)) {
- await expect(page.getByText("Successfully deleted category")).toBeVisible();
-
- await page
- .locator("li")
- .filter({ hasText: "Category-10" })
- .getByRole("button")
- .nth(1)
- .click();
- const modalName = page.url().split("#")[1];
- await page
- .locator(`div[id="${modalName}"] button[type="submit"]`)
- .evaluate((node) => node.removeAttribute("disabled"));
- await page.getByRole("button", { name: "Delete category" }).click();
-
- if (permissionLevel === 20) {
- await expect(page.getByText("Insufficient permissions")).toBeVisible();
- } else {
- await expect(page.getByText("Successfully deleted category")).toBeVisible();
- }
- }
- });
- test("Publish website", async ({ page }) => {
- await page.getByRole("link", { name: "Blog" }).click();
- await page.getByRole("link", { name: "Publish" }).click();
- await page
- .locator('form[action="?/publishWebsite"] button[type="submit"]')
- .evaluate((node) => node.removeAttribute("disabled"));
- await page.getByRole("button", { name: "Publish" }).click();
-
- if ([10, 20].includes(permissionLevel)) {
- await expect(page.getByText("Insufficient permissions")).toBeVisible();
- } else {
- await expect(page.getByText("Successfully published website")).toBeVisible();
- }
- });
- test("Set custom domain prefix", async ({ page }) => {
- await page.getByRole("link", { name: "Blog" }).click();
- await page.getByRole("link", { name: "Publish" }).click();
-
- const isMac = platform() === "darwin";
- const modifier = isMac ? "Meta" : "Control";
- await page.getByLabel("Prefix:").click();
- await page.keyboard.press(`${modifier}+A`);
- await page.keyboard.press(`Backspace`);
- await page.getByLabel("Prefix:").fill(customPrefix2);
- await page
- .locator('form[action="?/createUpdateCustomDomainPrefix"] button[type="submit"]')
- .evaluate((node) => node.removeAttribute("disabled"));
- await page.getByRole("button", { name: "Submit" }).click();
-
- if ([10, 20].includes(permissionLevel)) {
- await expect(page.getByText("Insufficient permissions")).toBeVisible();
- } else {
- await expect(page.getByText("Successfully created/updated")).toBeVisible();
- }
- });
- test("Remove custom domain prefix", async ({ page }) => {
- await page.getByRole("link", { name: "Blog" }).click();
- await page.getByRole("link", { name: "Publish" }).click();
- await page.getByRole("button", { name: "Delete" }).click();
- await page
- .locator('form[action="?/deleteCustomDomainPrefix"] button[type="submit"]')
- .evaluate((node) => node.removeAttribute("disabled"));
- await page.getByRole("button", { name: "Delete domain prefix" }).click();
-
- if ([10, 20].includes(permissionLevel)) {
- await expect(page.getByText("Insufficient permissions")).toBeVisible();
- } else {
- await expect(page.getByText("Successfully deleted domain")).toBeVisible();
- }
- });
- });
- }
-
- test("Delete all accounts", async ({ page }) => {
- await page.goto("/login");
-
- await page.getByLabel("Username:").fill(username);
- await page.getByLabel("Password:").fill(password);
- await page.getByRole("button", { name: "Submit" }).click();
- await page.getByRole("link", { name: "Account" }).click();
- await page.getByRole("button", { name: "Delete account" }).click();
- await page.getByLabel("Password:").click();
- await page.getByLabel("Password:").fill(password);
- await page
- .locator("#delete-account-modal")
- .getByRole("button", { name: "Delete account" })
- .click();
-
- await page.getByLabel("Username:").fill(collabUsername);
- await page.getByLabel("Password:").fill(password);
- await page.getByRole("button", { name: "Submit" }).click();
- await page.getByRole("link", { name: "Account" }).click();
- await page.getByRole("button", { name: "Delete account" }).click();
- await page.getByLabel("Password:").click();
- await page.getByLabel("Password:").fill(password);
- await page
- .locator("#delete-account-modal")
- .getByRole("button", { name: "Delete account" })
- .click();
-
- await page.getByLabel("Username:").fill(collabUsername2);
- await page.getByLabel("Password:").fill(password);
- await page.getByRole("button", { name: "Submit" }).click();
- await page.getByRole("link", { name: "Account" }).click();
- await page.getByRole("button", { name: "Delete account" }).click();
- await page.getByLabel("Password:").click();
- await page.getByLabel("Password:").fill(password);
- await page
- .locator("#delete-account-modal")
- .getByRole("button", { name: "Delete account" })
- .click();
-
- await page.getByLabel("Username:").fill(collabUsername3);
- await page.getByLabel("Password:").fill(password);
- await page.getByRole("button", { name: "Submit" }).click();
- await page.getByRole("link", { name: "Account" }).click();
- await page.getByRole("button", { name: "Delete account" }).click();
- await page.getByLabel("Password:").click();
- await page.getByLabel("Password:").fill(password);
- await page
- .locator("#delete-account-modal")
- .getByRole("button", { name: "Delete account" })
- .click();
-
- await page.getByLabel("Username:").fill(collabUsername4);
- await page.getByLabel("Password:").fill(password);
- await page.getByRole("button", { name: "Submit" }).click();
- await page.getByRole("link", { name: "Account" }).click();
- await page.getByRole("button", { name: "Delete account" }).click();
- await page.getByLabel("Password:").click();
- await page.getByLabel("Password:").fill(password);
- await page
- .locator("#delete-account-modal")
- .getByRole("button", { name: "Delete account" })
- .click();
- });
-});
diff --git a/web-app/tests/collaborators.spec.ts b/web-app/tests/collaborators.spec.ts
new file mode 100644
index 0000000..389c077
--- /dev/null
+++ b/web-app/tests/collaborators.spec.ts
@@ -0,0 +1,209 @@
+import { test, expect } from "@playwright/test";
+import { randomBytes, randomInt, type UUID } from "node:crypto";
+import {
+ userOwner,
+ register,
+ authenticate,
+ permissionLevels,
+ collabUsers,
+ collabTestingWebsite,
+ userCollab10,
+ userCollab20,
+ userCollab30
+} from "./shared";
+
+const genUsername = () => randomBytes(8).toString("hex") as UUID;
+const pickPermissionLevel = () => permissionLevels[randomInt(permissionLevels.length)].toString();
+
+test.describe("Website owner", () => {
+ test(`Add collaborator`, async ({ page }) => {
+ const addUsername = genUsername();
+
+ await register(addUsername, page);
+ await authenticate(userOwner, page);
+ await page
+ .locator("li")
+ .filter({ hasText: collabTestingWebsite })
+ .getByRole("link", { name: collabTestingWebsite })
+ .click();
+ await page.getByRole("link", { name: "Collaborators" }).click();
+
+ await page.getByRole("button", { name: "Add collaborator" }).click();
+ await page.locator("#add-collaborator-modal").getByLabel("Username:").click();
+ await page.locator("#add-collaborator-modal").getByLabel("Username:").fill(addUsername);
+ await page
+ .locator("#add-collaborator-modal")
+ .getByLabel("Permission level:")
+ .selectOption(pickPermissionLevel());
+ await page
+ .locator("#add-collaborator-modal")
+ .getByRole("button", { name: "Add collaborator" })
+ .click();
+ await expect(page.getByText("Successfully added collaborator")).toBeVisible();
+ await expect(page.getByRole("link", { name: "All collaborators" })).toBeVisible();
+ });
+
+ test.describe("Modify", () => {
+ let modifyUsername: UUID;
+
+ test.beforeEach(async ({ page }) => {
+ modifyUsername = genUsername();
+ await register(modifyUsername, page);
+ await authenticate(userOwner, page);
+ await page
+ .locator("li")
+ .filter({ hasText: collabTestingWebsite })
+ .getByRole("link", { name: collabTestingWebsite })
+ .click();
+ await page.getByRole("link", { name: "Collaborators" }).click();
+ await page.getByRole("button", { name: "Add collaborator" }).click();
+ await page.locator("#add-collaborator-modal").getByLabel("Username:").click();
+ await page.locator("#add-collaborator-modal").getByLabel("Username:").fill(modifyUsername);
+ await page
+ .locator("#add-collaborator-modal")
+ .getByLabel("Permission level:")
+ .selectOption(pickPermissionLevel());
+ await page
+ .locator("#add-collaborator-modal")
+ .getByRole("button", { name: "Add collaborator" })
+ .click();
+ });
+
+ test(`Update collaborator`, async ({ page }) => {
+ await page
+ .locator("li")
+ .filter({ hasText: modifyUsername })
+ .getByRole("button", { name: "Update" })
+ .first()
+ .click();
+ const modalName = page.url().split("#")[1];
+ await page
+ .locator(`#${modalName}`)
+ .getByLabel("Permission level:")
+ .selectOption(pickPermissionLevel());
+ await page.getByRole("button", { name: "Update collaborator" }).click();
+ await expect(page.getByText("Successfully updated collaborator")).toBeVisible();
+ });
+
+ test(`Remove collaborator`, async ({ page }) => {
+ await page
+ .locator("li")
+ .filter({ hasText: modifyUsername })
+ .getByRole("button", { name: "Remove" })
+ .first()
+ .click();
+ await page.getByRole("button", { name: "Remove collaborator" }).click();
+ await expect(page.getByText("Successfully removed collaborator")).toBeVisible();
+ });
+ });
+});
+
+for (const permissionLevel of permissionLevels) {
+ test.describe(`Website collaborator (Permission level: ${permissionLevel})`, () => {
+ test(`Add collaborator`, async ({ page }) => {
+ const addUsername = genUsername();
+
+ await register(addUsername, page);
+ await authenticate(collabUsers.get(permissionLevel)!, page);
+ await page
+ .locator("li")
+ .filter({ hasText: collabTestingWebsite })
+ .getByRole("link", { name: collabTestingWebsite })
+ .click();
+ await page.getByRole("link", { name: "Collaborators" }).click();
+
+ await page.getByRole("button", { name: "Add collaborator" }).click();
+ await page.locator("#add-collaborator-modal").getByLabel("Username:").click();
+ await page.locator("#add-collaborator-modal").getByLabel("Username:").fill(addUsername);
+ await page
+ .locator("#add-collaborator-modal")
+ .getByLabel("Permission level:")
+ .selectOption(pickPermissionLevel());
+ await page
+ .locator("#add-collaborator-modal")
+ .getByRole("button", { name: "Add collaborator" })
+ .evaluate((node) => node.removeAttribute("disabled"));
+ await page
+ .locator("#add-collaborator-modal")
+ .getByRole("button", { name: "Add collaborator" })
+ .click();
+
+ if ([10, 20].includes(permissionLevel)) {
+ await expect(page.getByText("Insufficient permissions")).toBeVisible();
+ } else {
+ await expect(
+ page
+ .getByText("Successfully added collaborator")
+ .or(page.getByText("Insufficient permissions"))
+ ).toBeVisible();
+ }
+ });
+
+ test(`Update collaborator`, async ({ page }) => {
+ await authenticate(collabUsers.get(permissionLevel)!, page);
+ await page
+ .locator("li")
+ .filter({ hasText: collabTestingWebsite })
+ .getByRole("link", { name: collabTestingWebsite })
+ .click();
+ await page.getByRole("link", { name: "Collaborators" }).click();
+
+ await page
+ .locator("li")
+ .filter({ hasNotText: new RegExp(`${userCollab10}|${userCollab20}|${userCollab30}`) })
+ .getByRole("button", { name: "Update" })
+ .first()
+ .click();
+ const modalName = page.url().split("#")[1];
+ await page
+ .locator(`#${modalName}`)
+ .getByLabel("Permission level:")
+ .selectOption(pickPermissionLevel());
+ await page
+ .getByRole("button", { name: "Update collaborator" })
+ .evaluate((node) => node.removeAttribute("disabled"));
+ await page.getByRole("button", { name: "Update collaborator" }).click();
+
+ if ([10, 20].includes(permissionLevel)) {
+ await expect(page.getByText("Insufficient permissions")).toBeVisible();
+ } else {
+ await expect(
+ page
+ .getByText("Successfully updated collaborator")
+ .or(page.getByText("Insufficient permissions"))
+ ).toBeVisible();
+ }
+ });
+
+ test(`Remove collaborator`, async ({ page }) => {
+ await authenticate(collabUsers.get(permissionLevel)!, page);
+ await page
+ .locator("li")
+ .filter({ hasText: collabTestingWebsite })
+ .getByRole("link", { name: collabTestingWebsite })
+ .click();
+ await page.getByRole("link", { name: "Collaborators" }).click();
+
+ await page
+ .locator("li")
+ .filter({ hasNotText: new RegExp(`${userCollab10}|${userCollab20}|${userCollab30}`) })
+ .getByRole("button", { name: "Remove" })
+ .first()
+ .click();
+ await page
+ .getByRole("button", { name: "Remove collaborator" })
+ .evaluate((node) => node.removeAttribute("disabled"));
+ await page.getByRole("button", { name: "Remove collaborator" }).click();
+
+ if ([10, 20].includes(permissionLevel)) {
+ await expect(page.getByText("Insufficient permissions")).toBeVisible();
+ } else {
+ await expect(
+ page
+ .getByText("Successfully removed collaborator")
+ .or(page.getByText("Insufficient permissions"))
+ ).toBeVisible();
+ }
+ });
+ });
+}
diff --git a/web-app/tests/dashboard.spec.ts b/web-app/tests/dashboard.spec.ts
new file mode 100644
index 0000000..53b8952
--- /dev/null
+++ b/web-app/tests/dashboard.spec.ts
@@ -0,0 +1,106 @@
+import { test, expect } from "@playwright/test";
+import {
+ userOwner,
+ authenticate,
+ permissionLevels,
+ collabUsers,
+ collabTestingWebsite
+} from "./shared";
+
+test.describe("Website owner", () => {
+ test.beforeEach(async ({ page }) => {
+ await authenticate(userOwner, page);
+ });
+
+ test(`Create website`, async ({ page }) => {
+ await page.getByRole("button", { name: "Create website" }).click();
+ await page.getByLabel("Type:").selectOption("Blog");
+ await page.locator("#create-website-modal").getByLabel("Title:").click();
+ await page.locator("#create-website-modal").getByLabel("Title:").fill("Blog");
+ await page
+ .locator("#create-website-modal")
+ .getByRole("button", { name: "Create website" })
+ .click();
+ const successCreation = page.getByText("Successfully created website");
+ const limitExceeded = page.getByText("Limit of 3 websites exceeded");
+ await expect(successCreation.or(limitExceeded)).toBeVisible();
+ await expect(page.getByRole("link", { name: "All websites" })).toBeVisible();
+ });
+
+ test.describe("Modify", () => {
+ test.beforeEach(async ({ page }) => {
+ await page.getByRole("button", { name: "Create website" }).click();
+ await page.getByLabel("Type:").selectOption("Blog");
+ await page.locator("#create-website-modal").getByLabel("Title:").click();
+ await page.locator("#create-website-modal").getByLabel("Title:").fill("Blog");
+ await page
+ .locator("#create-website-modal")
+ .getByRole("button", { name: "Create website" })
+ .click();
+ });
+
+ test(`Update website`, async ({ page }) => {
+ await page
+ .locator("li")
+ .filter({ hasNotText: collabTestingWebsite })
+ .getByRole("button", { name: "Update" })
+ .first()
+ .click();
+ const modalName = page.url().split("#")[1];
+ await page.locator(`#${modalName}`).getByLabel("Title:").click();
+ await page.locator(`#${modalName}`).getByLabel("Title:").fill(`${"Blog"} updated`);
+ await page.getByRole("button", { name: "Update website" }).click();
+ await expect(page.getByText("Successfully updated website")).toBeVisible();
+ });
+ test(`Delete website`, async ({ page }) => {
+ await page
+ .locator("li")
+ .filter({ hasNotText: collabTestingWebsite })
+ .getByRole("button", { name: "Delete" })
+ .first()
+ .click();
+ await page.getByRole("button", { name: "Delete website" }).click();
+ await expect(page.getByText("Successfully deleted website")).toBeVisible();
+ });
+ });
+});
+
+for (const permissionLevel of permissionLevels) {
+ test.describe(`Website collaborator (Permission level: ${permissionLevel})`, () => {
+ test.beforeEach(async ({ page }) => {
+ await authenticate(collabUsers.get(permissionLevel)!, page);
+ });
+
+ test("Update website", async ({ page }) => {
+ await page
+ .locator("li")
+ .filter({ hasText: collabTestingWebsite })
+ .getByRole("button", { name: "Update" })
+ .click();
+ await page
+ .getByRole("button", { name: "Update website" })
+ .evaluate((node) => node.removeAttribute("disabled"));
+ await page.getByRole("button", { name: "Update website" }).click();
+
+ if ([10, 20].includes(permissionLevel)) {
+ await expect(page.getByText("Insufficient permissions")).toBeVisible();
+ } else {
+ await expect(page.getByText("Successfully updated website")).toBeVisible();
+ }
+ });
+
+ test("Delete website", async ({ page }) => {
+ await page
+ .locator("li")
+ .filter({ hasText: collabTestingWebsite })
+ .getByRole("button", { name: "Delete" })
+ .click();
+ await page
+ .getByRole("button", { name: "Delete website" })
+ .evaluate((node) => node.removeAttribute("disabled"));
+ await page.getByRole("button", { name: "Delete website" }).click();
+
+ await expect(page.getByText("Insufficient permissions")).toBeVisible();
+ });
+ });
+}
diff --git a/web-app/tests/global-setup.ts b/web-app/tests/global-setup.ts
new file mode 100644
index 0000000..254b10e
--- /dev/null
+++ b/web-app/tests/global-setup.ts
@@ -0,0 +1,64 @@
+import { test } from "@playwright/test";
+import {
+ allUsers,
+ register,
+ authenticate,
+ userOwner,
+ collabTestingWebsite,
+ permissionLevels,
+ collabUsers,
+ userDummy
+} from "./shared";
+
+for (const username of allUsers) {
+ test(`Register user "${username}`, async ({ page }) => {
+ await register(username, page);
+ });
+}
+
+test.describe("Collaborator testing website", () => {
+ test.beforeEach(async ({ page }) => {
+ await authenticate(userOwner, page);
+ });
+
+ test("Create website", async ({ page }) => {
+ await page.getByRole("button", { name: "Create website" }).click();
+ await page.getByLabel("Type:").selectOption("Docs");
+ await page.locator("#create-website-modal").getByLabel("Title:").click();
+ await page.locator("#create-website-modal").getByLabel("Title:").fill(collabTestingWebsite);
+ await page
+ .locator("#create-website-modal")
+ .getByRole("button", { name: "Create website" })
+ .click();
+ });
+
+ for (const permissionLevel of permissionLevels) {
+ test(`Add collaborator "${collabUsers.get(permissionLevel)}"`, async ({ page }) => {
+ await page.getByRole("link", { name: collabTestingWebsite }).click();
+ await page.getByRole("link", { name: "Collaborators" }).click();
+ await page.getByRole("button", { name: "Add collaborator" }).click();
+ await page.getByLabel("Username:").click();
+ await page.getByLabel("Username:").fill(collabUsers.get(permissionLevel)!);
+ await page
+ .locator("#add-collaborator-modal")
+ .getByLabel("Permission level:")
+ .selectOption(permissionLevel.toString());
+ await page
+ .locator("#add-collaborator-modal")
+ .getByRole("button", { name: "Add collaborator" })
+ .click();
+ });
+ }
+});
+
+test("Dummy user website", async ({ page }) => {
+ await authenticate(userDummy, page);
+ await page.getByRole("button", { name: "Create website" }).click();
+ await page.getByLabel("Type:").selectOption("Blog");
+ await page.locator("#create-website-modal").getByLabel("Title:").click();
+ await page.locator("#create-website-modal").getByLabel("Title:").fill("Dummy");
+ await page
+ .locator("#create-website-modal")
+ .getByRole("button", { name: "Create website" })
+ .click();
+});
diff --git a/web-app/tests/global-teardown.ts b/web-app/tests/global-teardown.ts
new file mode 100644
index 0000000..fae68af
--- /dev/null
+++ b/web-app/tests/global-teardown.ts
@@ -0,0 +1,31 @@
+import { test } from "@playwright/test";
+import { password, authenticate, userOwner } from "./shared";
+
+test.beforeEach(async ({ page }) => {
+ await authenticate(userOwner, page);
+});
+
+/* test("Delete all regular users", async ({ page }) => {
+ await page.getByRole("link", { name: "Manage" }).click();
+
+ await page.waitForSelector("tbody");
+ const userRows = await page.locator("tbody > tr").filter({ hasNotText: userOwner }).all();
+
+ for (const row of userRows) {
+ await row.getByRole("button", { name: "Manage" }).click();
+ const modalName = page.url().split("#")[1];
+ await page.locator(`#${modalName}`).locator('summary:has-text("Delete")').click();
+ await page.locator(`#${modalName}`).getByRole("button", { name: "Delete user" }).click();
+ }
+});
+
+test("Delete admin account", async ({ page }) => {
+ await page.getByRole("link", { name: "Account" }).click();
+ await page.getByRole("button", { name: "Delete account" }).click();
+ await page.getByLabel("Password:").click();
+ await page.getByLabel("Password:").fill(password);
+ await page
+ .locator("#delete-account-modal")
+ .getByRole("button", { name: "Delete account" })
+ .click();
+}); */
diff --git a/web-app/tests/legal-information.spec.ts b/web-app/tests/legal-information.spec.ts
new file mode 100644
index 0000000..e2e23ea
--- /dev/null
+++ b/web-app/tests/legal-information.spec.ts
@@ -0,0 +1,109 @@
+import { test, expect } from "@playwright/test";
+import {
+ userOwner,
+ authenticate,
+ permissionLevels,
+ collabUsers,
+ collabTestingWebsite
+} from "./shared";
+
+test.describe("Website owner", () => {
+ test.beforeEach(async ({ page }) => {
+ await authenticate(userOwner, page);
+ await page
+ .locator("li")
+ .filter({ hasText: collabTestingWebsite })
+ .getByRole("link", { name: collabTestingWebsite })
+ .click();
+ await page.getByRole("link", { name: "Legal information" }).click();
+ });
+
+ test(`Create/update legal information`, async ({ page }) => {
+ await page.getByLabel("Main content:").click();
+ await page.getByLabel("Main content:").press("ControlOrMeta+a");
+ await page.getByLabel("Main content:").fill("## Content");
+ await page.getByRole("button", { name: "Update legal information" }).click();
+ await expect(page.getByText("Successfully created/updated legal information")).toBeVisible();
+ });
+
+ test(`Delete legal information`, async ({ page }) => {
+ await page.getByLabel("Main content:").click();
+ await page.getByLabel("Main content:").press("ControlOrMeta+a");
+ await page.getByLabel("Main content:").fill("## Arbitrary content");
+ await page.getByRole("button", { name: "Update legal information" }).click();
+
+ await page.getByRole("button", { name: "Delete" }).click();
+ await page.getByRole("button", { name: "Delete legal information" }).click();
+ await expect(page.getByText("Successfully deleted legal information")).toBeVisible();
+ });
+});
+
+for (const permissionLevel of permissionLevels) {
+ test.describe(`Website collaborator (Permission level: ${permissionLevel})`, () => {
+ test(`Create/update legal information`, async ({ page }) => {
+ await authenticate(collabUsers.get(permissionLevel)!, page);
+ await page
+ .locator("li")
+ .filter({ hasText: collabTestingWebsite })
+ .getByRole("link", { name: collabTestingWebsite })
+ .click();
+ await page.getByRole("link", { name: "Legal information" }).click();
+
+ await page.getByLabel("Main content:").click();
+ await page.getByLabel("Main content:").press("ControlOrMeta+a");
+ await page.getByLabel("Main content:").fill("## Random content");
+ await page
+ .getByRole("button", { name: "Update legal information" })
+ .evaluate((node) => node.removeAttribute("disabled"));
+ await page.getByRole("button", { name: "Update legal information" }).click();
+
+ if ([10, 20].includes(permissionLevel)) {
+ await expect(page.getByText("Insufficient permissions")).toBeVisible();
+ } else {
+ await expect(
+ page.getByText("Successfully created/updated legal information")
+ ).toBeVisible();
+ }
+ });
+
+ test(`Delete legal information`, async ({ page, browserName }) => {
+ test.skip(browserName === "firefox", "Some issues with Firefox in headful mode");
+
+ await authenticate(userOwner, page);
+ await page
+ .locator("li")
+ .filter({ hasText: collabTestingWebsite })
+ .getByRole("link", { name: collabTestingWebsite })
+ .click();
+ await page.getByRole("link", { name: "Legal information" }).click();
+
+ await page.getByLabel("Main content:").click();
+ await page.getByLabel("Main content:").press("ControlOrMeta+a");
+ await page.getByLabel("Main content:").fill("## Even more content");
+ await page.getByRole("button", { name: "Update legal information" }).click();
+ await page.waitForResponse(/createUpdateLegalInformation/);
+ await page.getByRole("link", { name: "Account" }).click();
+ await page.getByRole("button", { name: "Logout" }).click();
+
+ await authenticate(collabUsers.get(permissionLevel)!, page);
+ await page
+ .locator("li")
+ .filter({ hasText: collabTestingWebsite })
+ .getByRole("link", { name: collabTestingWebsite })
+ .click();
+ await page.getByRole("link", { name: "Legal information" }).click();
+
+ await page.getByRole("button", { name: "Delete" }).click();
+ await page
+ .getByRole("button", { name: "Delete legal information" })
+ .evaluate((node) => node.removeAttribute("disabled"));
+ await page.getByRole("button", { name: "Delete legal information" }).click();
+
+ if ([10, 20].includes(permissionLevel)) {
+ await expect(page.getByText("Insufficient permissions")).toBeVisible();
+ } else {
+ await expect(page.getByText("Successfully deleted legal information")).toBeVisible();
+ }
+ });
+ });
+}
diff --git a/web-app/tests/manage.spec.ts b/web-app/tests/manage.spec.ts
new file mode 100644
index 0000000..569f8bd
--- /dev/null
+++ b/web-app/tests/manage.spec.ts
@@ -0,0 +1,67 @@
+import { test, expect } from "@playwright/test";
+import { userOwner, userDummy, register, authenticate } from "./shared";
+
+const userDeleted = "test-deleted-m";
+
+test(`Update user website limit`, async ({ page }) => {
+ await authenticate(userOwner, page);
+ await page.getByRole("link", { name: "Manage" }).click();
+ await page
+ .locator("tr")
+ .filter({ hasNotText: userOwner })
+ .getByRole("button", { name: "Manage" })
+ .first()
+ .click();
+ const modalName = page.url().split("#")[1];
+ await page.locator(`#${modalName}`).getByLabel("Number of websites allowed:").click();
+ await page.locator(`#${modalName}`).getByLabel("Number of websites allowed:").fill("5");
+ await page.getByRole("button", { name: "Update website limit" }).click();
+ await expect(page.getByText("Successfully updated user website limit")).toBeVisible();
+});
+
+test(`Update user website storage limit`, async ({ page }) => {
+ await authenticate(userOwner, page);
+ await page.getByRole("link", { name: "Manage" }).click();
+ await page
+ .locator("tr")
+ .filter({ hasText: userDummy })
+ .getByRole("button", { name: "Manage" })
+ .first()
+ .click();
+ const modalName = page.url().split("#")[1];
+ await page.locator(`#${modalName}`).locator("details > summary").first().click();
+ await page
+ .locator(`#${modalName}`)
+ .locator("details")
+ .getByLabel("Storage limit in MB:")
+ .first()
+ .click();
+ await page
+ .locator(`#${modalName}`)
+ .locator("details")
+ .getByLabel("Storage limit in MB:")
+ .first()
+ .fill("555");
+ await page
+ .locator(`#${modalName}`)
+ .locator("details")
+ .getByRole("button", { name: "Update storage limit" })
+ .click();
+ await expect(page.getByText("Successfully updated user website storage size")).toBeVisible();
+});
+
+test(`Delete user`, async ({ page }) => {
+ await register(userDeleted, page);
+ await authenticate(userOwner, page);
+ await page.getByRole("link", { name: "Manage" }).click();
+ await page
+ .locator("tr")
+ .filter({ hasText: userDeleted })
+ .getByRole("button", { name: "Manage" })
+ .first()
+ .click();
+ const modalName = page.url().split("#")[1];
+ await page.locator(`#${modalName}`).locator('summary:has-text("Delete")').click();
+ await page.locator(`#${modalName}`).getByRole("button", { name: "Delete user" }).click();
+ await expect(page.getByText("Successfully deleted user")).toBeVisible();
+});
diff --git a/web-app/tests/publish.spec.ts b/web-app/tests/publish.spec.ts
new file mode 100644
index 0000000..7ff4a82
--- /dev/null
+++ b/web-app/tests/publish.spec.ts
@@ -0,0 +1,135 @@
+import { test, expect } from "@playwright/test";
+import {
+ userOwner,
+ authenticate,
+ permissionLevels,
+ collabUsers,
+ collabTestingWebsite
+} from "./shared";
+
+test.describe("Website owner", () => {
+ test.beforeEach(async ({ page }) => {
+ await authenticate(userOwner, page);
+ await page
+ .locator("li")
+ .filter({ hasText: collabTestingWebsite })
+ .getByRole("link", { name: collabTestingWebsite })
+ .click();
+ await page.getByRole("link", { name: "Publish" }).click();
+ });
+
+ test(`Publish website`, async ({ page }) => {
+ await page.getByRole("button", { name: "Publish" }).click();
+ await expect(page.getByText("Successfully published website")).toBeVisible();
+ await expect(page.getByText("Your website is published at")).toBeVisible();
+ });
+
+ test(`Set custom domain prefix`, async ({ page }) => {
+ await page.getByLabel("Prefix:").click();
+ await page.getByLabel("Prefix:").press("ControlOrMeta+a");
+ await page.getByLabel("Prefix:").fill("example-prefix");
+ await page.getByRole("button", { name: "Update domain prefix" }).click();
+ await expect(page.getByText("Successfully created/updated domain prefix")).toBeVisible();
+ });
+
+ test(`Delete custom domain prefix`, async ({ page }) => {
+ await page.getByLabel("Prefix:").click();
+ await page.getByLabel("Prefix:").press("ControlOrMeta+a");
+ await page.getByLabel("Prefix:").fill("example-prefix");
+ await page.getByRole("button", { name: "Update domain prefix" }).click();
+
+ await page.getByRole("button", { name: "Delete" }).click();
+ await page.getByRole("button", { name: "Delete domain prefix" }).click();
+ await expect(page.getByText("Successfully deleted domain prefix")).toBeVisible();
+ });
+});
+
+for (const permissionLevel of permissionLevels) {
+ test.describe(`Website collaborator (Permission level: ${permissionLevel})`, () => {
+ test(`Publish website`, async ({ page }) => {
+ await authenticate(collabUsers.get(permissionLevel)!, page);
+ await page
+ .locator("li")
+ .filter({ hasText: collabTestingWebsite })
+ .getByRole("link", { name: collabTestingWebsite })
+ .click();
+ await page.getByRole("link", { name: "Publish" }).click();
+
+ await page
+ .getByRole("button", { name: "Publish" })
+ .evaluate((node) => node.removeAttribute("disabled"));
+ await page.getByRole("button", { name: "Publish" }).click();
+
+ if ([10, 20].includes(permissionLevel)) {
+ await expect(page.getByText("Insufficient permissions")).toBeVisible();
+ } else {
+ await expect(page.getByText("Successfully published website")).toBeVisible();
+ await expect(page.getByText("Your website is published at")).toBeVisible();
+ }
+ });
+
+ test(`Set custom domain prefix`, async ({ page }) => {
+ await authenticate(collabUsers.get(permissionLevel)!, page);
+ await page
+ .locator("li")
+ .filter({ hasText: collabTestingWebsite })
+ .getByRole("link", { name: collabTestingWebsite })
+ .click();
+ await page.getByRole("link", { name: "Publish" }).click();
+
+ await page.getByLabel("Prefix:").click();
+ await page.getByLabel("Prefix:").press("ControlOrMeta+a");
+ await page.getByLabel("Prefix:").fill("new-prefix");
+ await page
+ .getByRole("button", { name: "Update domain prefix" })
+ .evaluate((node) => node.removeAttribute("disabled"));
+ await page.getByRole("button", { name: "Update domain prefix" }).click();
+
+ if ([10, 20].includes(permissionLevel)) {
+ await expect(page.getByText("Insufficient permissions")).toBeVisible();
+ } else {
+ await expect(page.getByText("Successfully created/updated domain prefix")).toBeVisible();
+ }
+ });
+
+ test(`Delete custom domain prefix`, async ({ page, browserName }) => {
+ test.skip(browserName === "firefox", "Some issues with Firefox in headful mode");
+
+ await authenticate(userOwner, page);
+ await page
+ .locator("li")
+ .filter({ hasText: collabTestingWebsite })
+ .getByRole("link", { name: collabTestingWebsite })
+ .click();
+ await page.getByRole("link", { name: "Publish" }).click();
+
+ await page.getByLabel("Prefix:").click();
+ await page.getByLabel("Prefix:").press("ControlOrMeta+a");
+ await page.getByLabel("Prefix:").fill("new-prefix");
+ await page.getByRole("button", { name: "Update domain prefix" }).click();
+ await page.waitForResponse(/createUpdateCustomDomainPrefix/);
+ await page.getByRole("link", { name: "Account" }).click();
+ await page.getByRole("button", { name: "Logout" }).click();
+
+ await authenticate(collabUsers.get(permissionLevel)!, page);
+ await page
+ .locator("li")
+ .filter({ hasText: collabTestingWebsite })
+ .getByRole("link", { name: collabTestingWebsite })
+ .click();
+ await page.getByRole("link", { name: "Publish" }).click();
+
+ await page.getByRole("button", { name: "Delete" }).click();
+ await page
+ .getByRole("button", { name: "Delete domain prefix" })
+ .evaluate((node) => node.removeAttribute("disabled"));
+ await page.getByRole("button", { name: "Delete domain prefix" }).click();
+
+ if ([10, 20].includes(permissionLevel)) {
+ await expect(page.getByText("Insufficient permissions")).toBeVisible();
+ } else {
+ await expect(page.getByText("Successfully deleted domain prefix")).toBeVisible();
+ }
+ });
+ });
+}
diff --git a/web-app/tests/settings.spec.ts b/web-app/tests/settings.spec.ts
new file mode 100644
index 0000000..1c332f1
--- /dev/null
+++ b/web-app/tests/settings.spec.ts
@@ -0,0 +1,175 @@
+import { test, expect } from "@playwright/test";
+import { randomBytes } from "node:crypto";
+import { fileURLToPath } from "node:url";
+import { dirname, join } from "node:path";
+import {
+ userOwner,
+ authenticate,
+ permissionLevels,
+ collabUsers,
+ collabTestingWebsite
+} from "./shared";
+
+const genRandomHex = () => `#${randomBytes(3).toString("hex")}`;
+const __filename = fileURLToPath(import.meta.url);
+const __dirname = dirname(__filename);
+
+test.describe("Website owner", () => {
+ test.beforeEach(async ({ page }) => {
+ await authenticate(userOwner, page);
+ await page
+ .locator("li")
+ .filter({ hasText: collabTestingWebsite })
+ .getByRole("link", { name: collabTestingWebsite })
+ .click();
+ });
+
+ test("Update global", async ({ page, browserName }) => {
+ test.skip(browserName === "firefox", "Some issues with Firefox in headless mode");
+
+ await page.getByLabel("Background color dark theme:").click();
+ await page.getByLabel("Background color dark theme:").fill(genRandomHex());
+ await page.getByLabel("Background color light theme:").click();
+ await page.getByLabel("Background color light theme:").fill(genRandomHex());
+ await page.getByLabel("Accent color dark theme:").click();
+ await page.getByLabel("Accent color dark theme:").fill(genRandomHex());
+ await page.getByLabel("Accent color light theme:").click();
+ await page.getByLabel("Accent color light theme:").fill(genRandomHex());
+ await page.getByLabel("Favicon:").click();
+ await page
+ .getByLabel("Favicon:")
+ .setInputFiles(join(__dirname, "sample-files", "archtika-logo-512x512.png"));
+ await page.getByRole("button", { name: "Update global" }).click();
+ await expect(page.getByText("Successfully updated global")).toBeVisible();
+ });
+
+ test("Update header", async ({ page, browserName }) => {
+ test.skip(browserName === "firefox", "Some issues with Firefox in headless mode");
+
+ await page.getByLabel("Logo type:").selectOption("image");
+ await page.getByLabel("Logo text:").click();
+ await page.getByLabel("Logo text:").press("ControlOrMeta+a");
+ await page.getByLabel("Logo text:").fill("Logo text");
+ await page.getByLabel("Logo image:").click();
+ await page
+ .getByLabel("Logo image")
+ .setInputFiles(join(__dirname, "sample-files", "archtika-logo-512x512.png"));
+ await page.getByRole("button", { name: "Update header" }).click();
+ await expect(page.getByText("Successfully updated header")).toBeVisible();
+ });
+
+ test("Update home", async ({ page }) => {
+ await page.getByLabel("Description:").click();
+ await page.getByLabel("Description:").fill("Description comes here");
+ await page.getByLabel("Main content:").click();
+ await page.getByLabel("Main content:").press("ControlOrMeta+a");
+ await page.getByLabel("Main content:").fill("## Updated main content");
+ await page.getByRole("button", { name: "Update home" }).click();
+ await expect(page.getByText("Successfully updated home")).toBeVisible();
+ });
+
+ test("Update footer", async ({ page }) => {
+ await page.getByLabel("Additional text:").click();
+ await page.getByLabel("Additional text:").press("ControlOrMeta+a");
+ await page.getByLabel("Additional text:").fill("Updated footer content");
+ await page.getByRole("button", { name: "Update footer" }).click();
+ await expect(page.getByText("Successfully updated footer")).toBeVisible();
+ });
+});
+
+for (const permissionLevel of permissionLevels) {
+ test.describe(`Website collaborator (Permission level: ${permissionLevel})`, () => {
+ test.beforeEach(async ({ page }) => {
+ await authenticate(collabUsers.get(permissionLevel)!, page);
+ await page
+ .locator("li")
+ .filter({ hasText: collabTestingWebsite })
+ .getByRole("link", { name: collabTestingWebsite })
+ .click();
+ });
+
+ test("Update global", async ({ page, browserName }) => {
+ test.skip(browserName === "firefox", "Some issues with Firefox in headless mode");
+
+ await page.getByLabel("Background color dark theme:").click();
+ await page.getByLabel("Background color dark theme:").fill(genRandomHex());
+ await page.getByLabel("Background color light theme:").click();
+ await page.getByLabel("Background color light theme:").fill(genRandomHex());
+ await page.getByLabel("Accent color dark theme:").click();
+ await page.getByLabel("Accent color dark theme:").fill(genRandomHex());
+ await page.getByLabel("Accent color light theme:").click();
+ await page.getByLabel("Accent color light theme:").fill(genRandomHex());
+ await page.getByLabel("Favicon:").click();
+ await page
+ .getByLabel("Favicon:")
+ .setInputFiles(join(__dirname, "sample-files", "archtika-logo-512x512.png"));
+ await page
+ .getByRole("button", { name: "Update global" })
+ .evaluate((node) => node.removeAttribute("disabled"));
+ await page.getByRole("button", { name: "Update global" }).click();
+
+ if (permissionLevel === 10) {
+ await expect(page.getByText("Insufficient permissions")).toBeVisible();
+ } else {
+ await expect(page.getByText("Successfully updated global")).toBeVisible();
+ }
+ });
+
+ test("Update header", async ({ page, browserName }) => {
+ test.skip(browserName === "firefox", "Some issues with Firefox in headless mode");
+
+ await page.getByLabel("Logo type:").selectOption("image");
+ await page.getByLabel("Logo text:").click();
+ await page.getByLabel("Logo text:").press("ControlOrMeta+a");
+ await page.getByLabel("Logo text:").fill("Logo text");
+ await page.getByLabel("Logo image:").click();
+ await page
+ .getByLabel("Logo image")
+ .setInputFiles(join(__dirname, "sample-files", "archtika-logo-512x512.png"));
+ await page
+ .getByRole("button", { name: "Update header" })
+ .evaluate((node) => node.removeAttribute("disabled"));
+ await page.getByRole("button", { name: "Update header" }).click();
+
+ if (permissionLevel === 10) {
+ await expect(page.getByText("Insufficient permissions")).toBeVisible();
+ } else {
+ await expect(page.getByText("Successfully updated header")).toBeVisible();
+ }
+ });
+
+ test("Update home", async ({ page }) => {
+ await page.getByLabel("Description:").click();
+ await page.getByLabel("Description:").fill("Description comes here");
+ await page.getByLabel("Main content:").click();
+ await page.getByLabel("Main content:").press("ControlOrMeta+a");
+ await page.getByLabel("Main content:").fill("## Updated main content");
+ await page
+ .getByRole("button", { name: "Update home" })
+ .evaluate((node) => node.removeAttribute("disabled"));
+ await page.getByRole("button", { name: "Update home" }).click();
+
+ if (permissionLevel === 10) {
+ await expect(page.getByText("Insufficient permissions")).toBeVisible();
+ } else {
+ await expect(page.getByText("Successfully updated home")).toBeVisible();
+ }
+ });
+
+ test("Update footer", async ({ page }) => {
+ await page.getByLabel("Additional text:").click();
+ await page.getByLabel("Additional text:").press("ControlOrMeta+a");
+ await page.getByLabel("Additional text:").fill("Updated footer content");
+ await page
+ .getByRole("button", { name: "Update footer" })
+ .evaluate((node) => node.removeAttribute("disabled"));
+ await page.getByRole("button", { name: "Update footer" }).click();
+
+ if (permissionLevel === 10) {
+ await expect(page.getByText("Insufficient permissions")).toBeVisible();
+ } else {
+ await expect(page.getByText("Successfully updated footer")).toBeVisible();
+ }
+ });
+ });
+}
diff --git a/web-app/tests/shared.ts b/web-app/tests/shared.ts
new file mode 100644
index 0000000..47cbfce
--- /dev/null
+++ b/web-app/tests/shared.ts
@@ -0,0 +1,35 @@
+import type { Page } from "@playwright/test";
+
+export const userOwner = "test-owner";
+export const userCollab10 = "test-collab10";
+export const userCollab20 = "test-collab20";
+export const userCollab30 = "test-collab30";
+export const userDummy = "test-dummy";
+
+export const collabUsers = new Map([
+ [10, userCollab10],
+ [20, userCollab20],
+ [30, userCollab30]
+]);
+export const permissionLevels = [10, 20, 30];
+export const allUsers = [userOwner, userCollab10, userCollab20, userCollab30, userDummy];
+export const password = "T3stinguser?!";
+export const contentTypes = ["Blog", "Docs"];
+export const collabTestingWebsite = "Collaborator testing";
+
+export const register = async (username: string, page: Page) => {
+ await page.goto("/register");
+ await page.getByLabel("Username:").click();
+ await page.getByLabel("Username:").fill(username);
+ await page.getByLabel("Password:").click();
+ await page.getByLabel("Password:").fill(password);
+ await page.getByRole("button", { name: "Register" }).click();
+};
+export const authenticate = async (username: string, page: Page) => {
+ await page.goto("/login");
+ await page.getByLabel("Username:").click();
+ await page.getByLabel("Username:").fill(username);
+ await page.getByLabel("Password:").click();
+ await page.getByLabel("Password:").fill(password);
+ await page.getByRole("button", { name: "Login" }).click();
+};
diff --git a/web-app/tests/website.spec.ts b/web-app/tests/website.spec.ts
deleted file mode 100644
index 2dc2574..0000000
--- a/web-app/tests/website.spec.ts
+++ /dev/null
@@ -1,420 +0,0 @@
-import { test as base, expect, type Page } from "@playwright/test";
-import { fileURLToPath } from "node:url";
-import { dirname, join } from "node:path";
-import { randomBytes } from "node:crypto";
-import { platform } from "node:os";
-
-const __filename = fileURLToPath(import.meta.url);
-const __dirname = dirname(__filename);
-
-const username = randomBytes(8).toString("hex");
-const collabUsername = randomBytes(8).toString("hex");
-const customPrefix = Buffer.from(randomBytes(16).map((byte) => (byte % 26) + 97)).toString();
-const customPrefix2 = Buffer.from(randomBytes(16).map((byte) => (byte % 26) + 97)).toString();
-const password = "T3stuser??!!";
-
-const test = base.extend<{ authenticatedPage: Page }>({
- authenticatedPage: async ({ page }, use) => {
- await page.goto("/login");
- await page.getByLabel("Username:").fill(username);
- await page.getByLabel("Password:").fill(password);
- await page.getByRole("button", { name: "Submit" }).click();
- await use(page);
- }
-});
-
-test.describe.serial("Website tests", () => {
- test("Register", async ({ page }) => {
- await page.goto("/register");
-
- await page.getByLabel("Username:").click();
- await page.getByLabel("Username:").fill(username);
- await page.getByLabel("Password:").click();
- await page.getByLabel("Password:").fill(password);
- await page.getByRole("button", { name: "Submit" }).click();
-
- await page.getByLabel("Username:").click();
- await page.getByLabel("Username:").fill(collabUsername);
- await page.getByLabel("Password:").click();
- await page.getByLabel("Password:").fill(password);
- await page.getByRole("button", { name: "Submit" }).click();
- });
-
- test("Create websites", async ({ authenticatedPage: page }) => {
- await page.getByRole("button", { name: "Create website" }).click();
- await page.getByLabel("Title:").click();
- await page.getByLabel("Title:").fill("Blog");
- await page.getByRole("button", { name: "Submit" }).click();
- await expect(page.getByRole("link", { name: "All websites" })).toBeVisible();
- await expect(page.getByText("Search & Filter")).toBeVisible();
- await expect(page.getByText("Blog Type: Blog Created at:")).toBeVisible();
-
- await page.getByRole("button", { name: "Create website" }).click();
- await page.getByLabel("Type: BlogDocs").selectOption("Docs");
- await page.getByLabel("Title:").click();
- await page.getByLabel("Title:").fill("Documentation");
- await page.getByRole("button", { name: "Submit" }).click();
- await expect(page.getByRole("link", { name: "All websites" })).toBeVisible();
- await expect(page.getByText("Search & Filter")).toBeVisible();
- await expect(page.getByText("Documentation Type: Docs")).toBeVisible();
- });
-
- test("Update websites", async ({ authenticatedPage: page }) => {
- await page.locator("li").filter({ hasText: "Blog" }).getByRole("button").first().click();
- await page.getByRole("textbox", { name: "Title" }).click();
- await page.getByRole("textbox", { name: "Title" }).fill("Blog updated");
- await page.getByRole("button", { name: "Submit" }).click();
- await expect(page.getByRole("link", { name: "Blog updated" })).toBeVisible();
-
- await page
- .locator("li")
- .filter({ hasText: "Documentation" })
- .getByRole("button")
- .first()
- .click();
- await page.getByRole("textbox", { name: "Title" }).click();
- await page.getByRole("textbox", { name: "Title" }).fill("Documentation updated");
- await page.getByRole("button", { name: "Submit" }).click();
- await expect(page.getByRole("link", { name: "Documentation updated" })).toBeVisible();
- });
-
- test.describe.serial("Blog", () => {
- test.describe.serial("Update settings", () => {
- test("Global", async ({ authenticatedPage: page }) => {
- await page.getByRole("link", { name: "Blog" }).click();
- await page.getByLabel("Background color dark theme: ").click();
- await page.getByLabel("Background color dark theme:").fill("#3975a2");
- await page.getByLabel("Background color light theme:").click();
- await page.getByLabel("Background color light theme:").fill("#41473e");
- await page.getByLabel("Accent color dark theme: ").click();
- await page.getByLabel("Accent color dark theme:").fill("#3975a2");
- await page.getByLabel("Accent color light theme:").click();
- await page.getByLabel("Accent color light theme:").fill("#41473e");
- await page.locator("#global").getByRole("button", { name: "Submit" }).click();
- await expect(page.getByText("Successfully updated global")).toBeVisible();
- await page.getByLabel("Favicon:").click();
- await page
- .getByLabel("Favicon:")
- .setInputFiles(join(__dirname, "sample-files", "archtika-logo-512x512.png"));
- await page.locator("#global").getByRole("button", { name: "Submit" }).click();
- await expect(page.getByText("Successfully updated global")).toBeVisible();
- });
-
- test("Header", async ({ authenticatedPage: page }) => {
- await page.getByRole("link", { name: "Blog" }).click();
- await page.getByLabel("Logo text:").click();
- await page.getByLabel("Logo text:").fill("archtika Blog updated");
- await page.locator("#header").getByRole("button", { name: "Submit" }).click();
- await expect(page.getByText("Successfully updated header")).toBeVisible();
- await page.getByLabel("Logo type: TextImage").selectOption("image");
- await page.getByLabel("Logo image:").click();
- await page
- .getByLabel("Logo image:")
- .setInputFiles(join(__dirname, "sample-files", "archtika-logo-512x512.png"));
- await page.locator("#header").getByRole("button", { name: "Submit" }).click();
- await expect(page.getByText("Successfully updated header")).toBeVisible();
- });
-
- test("Home", async ({ authenticatedPage: page }) => {
- await page.getByRole("link", { name: "Blog" }).click();
- await page.getByLabel("Description:").click();
- await page.getByLabel("Description:").fill("Description");
- await page.getByLabel("Main content:").click();
- await page.getByLabel("Main content:").press("Control+a");
- await page.getByLabel("Main content:").fill("## Some new content comes here");
- await expect(page.getByRole("link", { name: "Some new content comes here" })).toBeVisible();
- await page.locator("#home").getByRole("button", { name: "Submit" }).click();
- await expect(page.getByText("Successfully updated home")).toBeVisible();
- });
-
- test("Footer", async ({ authenticatedPage: page }) => {
- await page.getByRole("link", { name: "Blog" }).click();
- await page.getByLabel("Additional text:").click();
- await page
- .getByLabel("Additional text:")
- .fill(
- "archtika is a free, open, modern, performant and lightweight CMS updated content comes here"
- );
- await page.locator("#footer").getByRole("button", { name: "Submit" }).click();
- await expect(page.getByText("Successfully updated footer")).toBeVisible();
- });
- });
-
- test.describe.serial("Articles", () => {
- test("Create article", async ({ authenticatedPage: page }) => {
- await page.getByRole("link", { name: "Blog" }).click();
- await page.getByRole("link", { name: "Articles" }).click();
- await page.getByRole("button", { name: "Create article" }).click();
- await page.getByLabel("Title:").click();
- await page.getByLabel("Title:").fill("Test article");
- await page.getByRole("button", { name: "Submit" }).click();
- await expect(page.getByRole("link", { name: "All articles" })).toBeVisible();
- await expect(page.getByText("Search & Filter")).toBeVisible();
- await expect(page.getByText("Test article Edit Delete")).toBeVisible();
- });
-
- test("Update article", async ({ authenticatedPage: page }) => {
- await page.getByRole("link", { name: "Blog" }).click();
- await page.getByRole("link", { name: "Articles" }).click();
- await page.getByRole("link", { name: "Edit" }).click();
- await page.getByLabel("Description:").click();
- await page.getByLabel("Description:").fill("Sample article description");
- await page.getByLabel("Author:").click();
- await page.getByLabel("Author:").fill("John Doe");
- await page.getByLabel("Main content:").click();
- await page
- .getByLabel("Main content:")
- .fill(
- "## Section\n\n### Subsection\n\n## Second section\n\n### Second subsection\n\n#### Sub Sub section"
- );
- await expect(
- page.getByText(
- "Table of contents SectionSubsectionSecond sectionSecond subsectionSub Sub"
- )
- ).toBeVisible();
- await expect(
- page.getByRole("heading", { name: "Section", exact: true }).getByRole("link")
- ).toBeVisible();
- await page.getByRole("button", { name: "Submit" }).click();
- await expect(page.getByText("Successfully updated article")).toBeVisible();
- });
-
- test("Paste image", async ({ authenticatedPage: page, context }) => {
- await page.getByRole("link", { name: "Blog" }).click();
- await page.getByRole("link", { name: "Articles" }).click();
- await page.getByRole("link", { name: "Edit" }).click();
- await page.getByLabel("Main content:").click();
-
- await context.grantPermissions(["clipboard-read", "clipboard-write"]);
- const isMac = platform() === "darwin";
- const modifier = isMac ? "Meta" : "Control";
-
- const clipPage = await context.newPage();
- await clipPage.goto("https://picsum.photos/400/400.jpg");
- await clipPage.keyboard.press(`${modifier}+C`);
-
- await page.bringToFront();
- await page.keyboard.press("Enter");
- await page.keyboard.press("Enter");
- await page.keyboard.press(`${modifier}+V`);
-
- await expect(page.getByText("Successfully uploaded image")).toBeVisible();
- });
-
- test("Delete article", async ({ authenticatedPage: page }) => {
- await page.getByRole("link", { name: "Blog" }).click();
- await page.getByRole("link", { name: "Articles" }).click();
- await page.getByRole("button", { name: "Delete" }).click();
- await page.getByRole("button", { name: "Delete article" }).click();
- await expect(page.getByText("Successfully deleted article")).toBeVisible();
- });
- });
-
- test.describe.serial("Collaborators", () => {
- test("Add collaborator", async ({ authenticatedPage: page }) => {
- await page.getByRole("link", { name: "Blog" }).click();
- await page.getByRole("link", { name: "Collaborators" }).click();
- await page.getByRole("button", { name: "Add collaborator" }).click();
- await page.getByLabel("Username:").click();
- await page.getByLabel("Username:").fill(collabUsername);
- await page.getByRole("button", { name: "Submit" }).click();
- await expect(page.getByText("Successfully added")).toBeVisible();
- });
-
- test("Update collaborator", async ({ authenticatedPage: page }) => {
- await page.getByRole("link", { name: "Blog" }).click();
- await page.getByRole("link", { name: "Collaborators" }).click();
- await page.getByRole("button", { name: "Update" }).click();
- await page.getByRole("combobox").selectOption("20");
- await page.getByRole("button", { name: "Update collaborator" }).click();
- await expect(page.getByText("Successfully updated")).toBeVisible();
- });
-
- test("Remove collaborator", async ({ authenticatedPage: page }) => {
- await page.getByRole("link", { name: "Blog" }).click();
- await page.getByRole("link", { name: "Collaborators" }).click();
- await page.getByRole("button", { name: "Remove" }).click();
- await page.getByRole("button", { name: "Remove collaborator" }).click();
- await expect(page.getByText("Successfully removed")).toBeVisible();
- });
- });
-
- test.describe.serial("Legal information", () => {
- test("Create/Update legal information", async ({ authenticatedPage: page }) => {
- await page.getByRole("link", { name: "Blog" }).click();
- await page.getByRole("link", { name: "Legal information" }).click();
- await page.getByLabel("Main content:").click();
- await page.getByLabel("Main content:").fill("## Content");
- await page.getByRole("button", { name: "Submit" }).click();
- await expect(page.getByText("Successfully created/updated legal")).toBeVisible();
-
- await page.getByLabel("Main content:").click();
- await page.getByLabel("Main content:").fill("## Content updated");
- await page.getByRole("button", { name: "Submit" }).click();
- await expect(page.getByText("Successfully created/updated legal")).toBeVisible();
- });
- test("Delete legal information", async ({ authenticatedPage: page }) => {
- await page.getByRole("link", { name: "Blog" }).click();
- await page.getByRole("link", { name: "Legal information" }).click();
- await page.getByRole("button", { name: "Delete" }).click();
- await page.getByRole("button", { name: "Delete legal information" }).click();
- await expect(page.getByText("Successfully deleted legal")).toBeVisible();
- });
- });
- });
-
- test.describe.serial("Docs", () => {
- test.describe.serial("Categories", () => {
- test("Create category", async ({ authenticatedPage: page }) => {
- await page.getByRole("link", { name: "Documentation" }).click();
- await page.getByRole("link", { name: "Categories" }).click();
- await page.getByRole("button", { name: "Create category" }).click();
- await page.getByLabel("Name:").nth(0).click();
- await page.getByLabel("Name:").nth(0).fill("Category");
- await page.getByLabel("Weight:").click();
- await page.getByLabel("Weight:").fill("1000");
- await page.getByRole("button", { name: "Submit" }).click();
- await expect(page.getByText("Successfully created category")).toBeVisible();
- await expect(page.getByRole("link", { name: "All categories" })).toBeVisible();
- await expect(page.getByText("Category (1000)")).toBeVisible();
- });
- test("Update category", async ({ authenticatedPage: page }) => {
- await page.getByRole("link", { name: "Documentation" }).click();
- await page.getByRole("link", { name: "Categories" }).click();
- await page.getByRole("button", { name: "Update" }).click();
- await page.getByRole("spinbutton", { name: "Weight:" }).click();
- await page.getByRole("spinbutton", { name: "Weight:" }).fill("500");
- await page.getByRole("button", { name: "Update category" }).click();
- await expect(page.getByText("Successfully updated category")).toBeVisible();
- await expect(page.getByText("Category (500)")).toBeVisible();
- });
- test("Delete category", async ({ authenticatedPage: page }) => {
- await page.getByRole("link", { name: "Documentation" }).click();
- await page.getByRole("link", { name: "Categories" }).click();
- await page.getByRole("button", { name: "Delete" }).click();
- await page.getByRole("button", { name: "Delete category" }).click();
- await expect(page.getByText("Successfully deleted category")).toBeVisible();
- await expect(page.getByRole("link", { name: "All categories" })).toBeHidden();
- });
- });
-
- test("Article", async ({ authenticatedPage: page }) => {
- await page.getByRole("link", { name: "Documentation" }).click();
- await page.getByRole("link", { name: "Categories" }).click();
- await page.getByRole("button", { name: "Create category" }).click();
- await page.getByLabel("Name:").nth(0).click();
- await page.getByLabel("Name:").nth(0).fill("Category");
- await page.getByLabel("Weight:").click();
- await page.getByLabel("Weight:").fill("1000");
- await page.getByRole("button", { name: "Submit" }).click();
- await page.getByRole("link", { name: "Articles" }).click();
- await page.getByRole("button", { name: "Create article" }).click();
- await page.getByLabel("Title:").click();
- await page.getByLabel("Title:").fill("Article");
- await page.getByRole("button", { name: "Submit" }).click();
- await page.getByRole("link", { name: "Edit" }).click();
- await page.getByLabel("Weight:").click();
- await page.getByLabel("Weight:").fill("1000");
- await page.getByLabel("Title:").click();
- await page.getByLabel("Title:").fill("Article");
- await page.getByLabel("Description:").click();
- await page.getByLabel("Description:").fill("Testing out this article");
- await page.getByLabel("Author:").click();
- await page.getByLabel("Author:").fill("John Doe");
- await page.getByLabel("Main content:").click();
- await page
- .getByLabel("Main content:")
- .fill(
- "## Main content comes in here\n\n### First section\n\n### Second section\n\n## More"
- );
- await page.getByRole("button", { name: "Submit" }).click();
- await expect(page.getByText("Successfully updated article")).toBeVisible();
- await expect(page.getByText("Table of contents Main")).toBeVisible();
- await expect(
- page.getByRole("heading", { name: "Main content comes in here" }).getByRole("link")
- ).toBeVisible();
- });
- });
-
- test("Publish websites", async ({ authenticatedPage: page }) => {
- await page.getByRole("link", { name: "Blog" }).click();
- await page.getByRole("link", { name: "Publish" }).click();
- await page.getByRole("button", { name: "Publish" }).click();
- await expect(page.getByText("Successfully published website")).toBeVisible();
- await expect(page.getByText("Your website is published at")).toBeVisible();
-
- await page.goto("/");
- await page.getByRole("link", { name: "Documentation" }).click();
- await page.getByRole("link", { name: "Publish" }).click();
- await page.getByRole("button", { name: "Publish" }).click();
- await expect(page.getByText("Successfully published website")).toBeVisible();
- await expect(page.getByText("Your website is published at")).toBeVisible();
- });
-
- test("Set custom domain prefixes", async ({ authenticatedPage: page }) => {
- await page.getByRole("link", { name: "Blog" }).click();
- await page.getByRole("link", { name: "Publish" }).click();
- await page.getByLabel("Prefix:").click();
- await page.getByLabel("Prefix:").fill(customPrefix);
- await page.getByRole("button", { name: "Submit" }).click();
- await expect(page.getByText("Successfully created/updated")).toBeVisible();
-
- await page.goto("/");
- await page.getByRole("link", { name: "Documentation" }).click();
- await page.getByRole("link", { name: "Publish" }).click();
- await page.getByLabel("Prefix:").click();
- await page.getByLabel("Prefix:").fill(customPrefix2);
- await page.getByRole("button", { name: "Submit" }).click();
- await expect(page.getByText("Successfully created/updated")).toBeVisible();
- });
-
- test("Remove custom domain prefixes", async ({ authenticatedPage: page }) => {
- await page.getByRole("link", { name: "Blog" }).click();
- await page.getByRole("link", { name: "Publish" }).click();
- await page.getByRole("button", { name: "Delete" }).click();
- await page.getByRole("button", { name: "Delete domain prefix" }).click();
- await expect(page.getByText("Successfully deleted domain")).toBeVisible();
-
- await page.goto("/");
- await page.getByRole("link", { name: "Documentation" }).click();
- await page.getByRole("link", { name: "Publish" }).click();
- await page.getByRole("button", { name: "Delete" }).click();
- await page.getByRole("button", { name: "Delete domain prefix" }).click();
- await expect(page.getByText("Successfully deleted domain")).toBeVisible();
- });
-
- test("Delete websites", async ({ authenticatedPage: page }) => {
- await page.getByRole("button", { name: "Delete" }).nth(1).click();
- await page.getByRole("button", { name: "Delete website" }).click();
- await expect(page.getByText("Successfully deleted website")).toBeVisible();
-
- await page.getByRole("button", { name: "Delete" }).click();
- await page.getByRole("button", { name: "Delete website" }).click();
- await expect(page.getByText("Successfully deleted website")).toBeVisible();
-
- await expect(page.getByRole("link", { name: "All websites" })).toBeHidden();
- });
-
- test("Delete accounts", async ({ authenticatedPage: page }) => {
- await page.getByRole("link", { name: "Account" }).click();
- await page.getByRole("button", { name: "Delete account" }).click();
- await page.getByLabel("Password:").click();
- await page.getByLabel("Password:").fill(password);
- await page
- .locator("#delete-account-modal")
- .getByRole("button", { name: "Delete account" })
- .click();
-
- await page.getByLabel("Username:").fill(collabUsername);
- await page.getByLabel("Password:").fill(password);
- await page.getByRole("button", { name: "Submit" }).click();
- await page.getByRole("link", { name: "Account" }).click();
- await page.getByRole("button", { name: "Delete account" }).click();
- await page.getByLabel("Password:").click();
- await page.getByLabel("Password:").fill(password);
- await page
- .locator("#delete-account-modal")
- .getByRole("button", { name: "Delete account" })
- .click();
- });
-});